//////////////////////////////////////////////////////////////////////
//
//  Crytek (C) 2001
//
//  CrySound Source Code
//
//  File: SoundSystem.cpp
//  Description: ISoundSystem interface implementation.
//
//  History:
//	-June 06,2001:Implemented by Marco Corbetta
//  -June 01,2005: Implemented group manager and made 3 script io routines NICK
//                3 routines are Precache, NewPlay, and NewPlayEx
//
//////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include "SoundSystem.h"
#include "SoundCVars.h"
#include <I3DEngine.h> //needed to check if the listener is in indoor or outdoor
#include "IRenderAuxGeom.h"
#include "ILocalizationManager.h" //Voice file
#include "Sound.h"
#include "MoodManager.h"
#include "IGameFramework.h" // musiclogic debug drawing
#include "MusicSystem/MusicSystem.h"
#include "FileCacheManager.h"

#ifdef SOUNDSYSTEM_USE_FMODEX400
#include "ReverbManagerEAX.h"
#include "Microphone.h"
#include "AudioDeviceFmodEx400.h"
#else
#include "ReverbManager.h"
#include "AudioDeviceFmodEx400_NULL.h"
#endif // SOUNDSYSTEM_USE_FMODEX400

#if !defined(EXCLUDE_SCALEFORM_SDK)





#endif

#ifdef INCLUDE_AUDIO_PRODUCTION_CODE
#include <IDebugHistory.h>
#endif // INCLUDE_AUDIO_PRODUCTION_CODE

unsigned int CSoundSystem::g_nBuffersTrashed;
int CSoundSystem::g_nOverallStreamsCount = 0;
//CRopeSurfaceCache* CSoundSystem::g_pRopeSurfaceCache = 0;

//ISoundAssetManager* CSoundSystem::m_pSoundAssetManager = NULL;
CryCriticalSection m_CSDebug;
int unsigned g_nStreamingDebugCount = 0;

// Declaration of extern memory pools
CSoundAllocator g_MemoryPoolPrimary;
#ifdef PROVIDE_SECONDARY_POOL
	tMemoryPoolReferenced g_MemoryPoolSoundSecondary;
#endif

// Static sound memory info struct
SSoundMemoryInfo CSoundSystem::g_oSoundMemoryInfo;

// Define global sound cvars
CSoundCVars g_SoundCVars;

//tMemoryPoolDynamic STLContainer;

static ILINE CSaltHandle<> IdToHandle( const tSoundID id ) { return CSaltHandle<>(id>>16,id&0xffff); }
static ILINE tSoundID HandleToId( const CSaltHandle<> id ) { return (((uint32)id.GetSalt())<<16) | ((uint32)id.GetIndex()); }

//static ILINE tSoundID HandleToId( const CSaltHandle<> id ) { return (((uint32)id.GetSalt())<<16) | ((uint32)id.GetIndex()); }
//////////////////////////////////////////////////////////////////////////
/*
void* _DSPUnit_SFXFilter_Callback(void *pOriginalBuffer, void *pNewBuffer, int nLength, INT_PTR nParam)
{
	if (!nParam)
		return pNewBuffer;
	CSoundSystem *pOwner=(CSoundSystem*)nParam;
	return pOwner->DSPUnit_SFXFilter_Callback(pOriginalBuffer, pNewBuffer, nLength);
}

*/

int OnObstructionTest(const EventPhys *pEvent)
{
	CSoundSystem *pSSys = (CSoundSystem*) gEnv->pSoundSystem;

	if (!pSSys->g_nObstruction)
		return 1;

	EventPhysRWIResult *pRWIResult = (EventPhysRWIResult*)pEvent;
	if (pRWIResult->iForeignData == PHYS_FOREIGN_ID_SOUND_OBSTRUCTION)
	{
		SObstructionTest* pObstructionTest = (SObstructionTest*) pRWIResult->pForeignData;

		if (pObstructionTest->SoundID != INVALID_SOUNDID)
		{
			_smart_ptr<CSound> pSound = (CSound*) pSSys->GetSound(pObstructionTest->SoundID);

			if (pSound && pSound->m_pObstruction) // check if the sound is still valid
			{
				pSound->m_pObstruction->bProcessed = false;
				pObstructionTest->nHits = pRWIResult->nHits;
				pObstructionTest->bResult = true;
				pObstructionTest->nPierceability = 0;

				if (pRWIResult->nHits)
				{
					float fDistance = FLT_MAX;
					ISurfaceTypeManager *pSurfaceTypeManager = gEnv->p3DEngine->GetMaterialManager()->GetSurfaceTypeManager();

					for (int j=0; j<10; j++)
						if (pRWIResult->pHits[j].dist > 0)
						{
							ISurfaceType *pMat = pSurfaceTypeManager->GetSurfaceType(pRWIResult->pHits[j].surface_idx);
							if (pMat)
								pObstructionTest->nPierceability += (15 - pMat->GetPhyscalParams().pierceability);

							fDistance = min(fDistance, pRWIResult->pHits[j].dist);
						}
						pObstructionTest->fDistance = fDistance;
				}

				bool bTempFirst = pSound->m_pObstruction->bFirstTime;
				pSound->m_pObstruction->bFirstTime = false;
				pSound->m_pObstruction->bDelayPlayback = false;
				pSound->m_pObstruction->bDontAveragePrevious = false;

				if (bTempFirst && !(pSound->GetFlags() & FLAG_SOUND_START_PAUSED))
				{
					pSound->m_pObstruction->bDontAveragePrevious = true;
					pSound->Play();
				}

				// removes AddRef on queued RWI
				pSound->Release();
			}
		}
	}

	return 1;
}






//////////////////////////////////////////////////////////////////////
CSoundSystem::CSoundSystem(void* hWnd, IAudioDevice *pAudioDevice)
:	CSoundSystemCommon(),
	m_pFileCacheManager(NULL),
	m_bEnteredGame(false)
{
	MEMSTAT_CONTEXT(EMemStatContextTypes::MSC_Other, 0, "SoundSystem Constructor");

	//m_nMemTest = 0;

	// Constructor deals with an UNINITIALIZED AudioDevice
	// Init() can rely on an initialized AudioDevice
	GUARD_HEAP;

	// Register audio cvars
	g_SoundCVars.RegisterVariables();

	assert(pAudioDevice!=NULL);


















	// set auto values for PC
	//g_nInternCacheStatic = 0; // off on PC by default
	//g_fInternCacheSize = 60;

	// a bit more for Editor, because we can have more precaching structures
	if (gEnv->IsEditor())
	{
		m_fInternMemoryPoolSoundPrimary = 50.0f;
	}
	else
	{
		m_fInternMemoryPoolSoundPrimary = 40.0f;
	}

	m_fInternMemoryPoolSoundSecondary = 0.0f;

	m_UpdateTicks = 0;

	
	// Primary Sound Memory Pool
	ICVar* pMemoryPoolSoundPrimary = gEnv->pConsole->GetCVar("s_MemoryPoolSoundPrimary");
	float fMemoryPoolSoundPrimarySize = pMemoryPoolSoundPrimary ? pMemoryPoolSoundPrimary->GetFVal() : 0.0f;
	fMemoryPoolSoundPrimarySize = (fMemoryPoolSoundPrimarySize > 0) ? fMemoryPoolSoundPrimarySize : m_fInternMemoryPoolSoundPrimary;

	if (fMemoryPoolSoundPrimarySize)
	{
		MEMSTAT_CONTEXT(EMemStatContextTypes::MSC_Other, 0, "Sound Memory Pool Primary");
		uint32 nPrimarySize		= (uint32)(fMemoryPoolSoundPrimarySize * 1024.0f * 1024.0f);
		uint8* pPrimaryMemory	= new uint8[nPrimarySize];
		g_MemoryPoolPrimary.InitMem(nPrimarySize, pPrimaryMemory);		
	}

	// Allocate for the file cache manager.
	POOL_NEW(CFileCacheManager, m_pFileCacheManager);

	if (m_pFileCacheManager)
	{
		ICVar const* const pFileCacheManagerSizeCVar = gEnv->pConsole->GetCVar("s_FileCacheManagerSize");
		size_t const nFileCacheManagerSize = pFileCacheManagerSizeCVar ? pFileCacheManagerSizeCVar->GetIVal() : 0;

		if (nFileCacheManagerSize > 0)
		{
			m_pFileCacheManager->AllocateHeap(nFileCacheManagerSize, "AudioFileCacheManager");
		}
	}

	// Secondary Sound Memory Pool
#ifdef PROVIDE_SECONDARY_POOL
	ICVar* pMemoryPoolSoundSecondary = gEnv->pConsole->GetCVar("s_MemoryPoolSoundSecondary");
	float fMemoryPoolSoundSecondarySize = pMemoryPoolSoundSecondary ? pMemoryPoolSoundSecondary->GetFVal() : 0.0f;
	fMemoryPoolSoundSecondarySize = (fMemoryPoolSoundSecondarySize > 0) ? fMemoryPoolSoundSecondarySize : m_fInternMemoryPoolSoundSecondary;

	if (fMemoryPoolSoundSecondarySize)
	{
		MEMSTAT_CONTEXT(EMemStatContextTypes::MSC_Other, 0, "Sound Memory Pool Secondary");

		uint32 nSecondarySize = (uint32)(fMemoryPoolSoundSecondarySize * 1024 * 1024);
		









		uint8* pSecondaryMemory = new uint8[nSecondarySize];

		g_MemoryPoolSoundSecondary.InitMem(nSecondarySize, (uint8*)pSecondaryMemory);
	}
#endif


	//tMemoryPoolDynamicSTLWrapped::Container(&g_MemoryPoolPrimary);
		//g_MemoryPoolPrimary.InitMem(TEST_STL_COUNT,g_pSTLData);


	//m_SoundMemory	=	*new (g_TestArea)TContainer;
	//void* P0	=	m_SoundMemoryS.Allocate<void*>(1024);

	//m_pNetworkSound = NULL;
	//m_pMicrophone = NULL;
	//int nTest = sizeof(CSound);
		
	m_bSoundSystemEnabled = true;
	m_bDelayPrecaching		= true;
	m_bOK									= false;
	m_bIsInPauseCall			= false;

	m_pAudioDevice				= NULL;
	m_pSoundAssetManager	= NULL;
	m_pReverbManager      = NULL;
  m_pMoodManager				= NULL;
	m_pDebugLogger				= NULL;
	m_MemUsage						= 0;
	m_ErrorCode						= eSoundSystemErrorCode_None;

	if (g_nDummySound)		
		return; // creates dummy sound system

	if (!pAudioDevice) 
		return;

	// setting the platform dependent AudioDevice implementation
	m_pAudioDevice = (CAudioDeviceFmodEx400*)pAudioDevice;

	m_tUpdateAudioDevice.SetMilliSeconds(0);
	m_tUpdateReverbManager.SetMilliSeconds(0);
	m_tUpdateFileCacheManager.SetMilliSeconds(0);
	m_tLastFrameTime.SetMilliSeconds(0);

	m_fNoFocusVolume      = 1.0f;
	m_fSFXVolume          =	1.0f;
	m_fGameSFXVolume      =	1.0f;
	m_fGameSFXRatio       = g_SoundCVars.g_fGameSFXVolume;
	m_fMusicVolume        =	1.0f;
	m_fGameMusicVolume    =	1.0f;
	m_fGameMusicRatio     = g_SoundCVars.g_fGameMusicVolume;
  m_fMusicEffectVolume  = 1.0f;
	m_fDialogVolume       = 1.0f;
	m_fGameDialogVolume   = 1.0f;
	m_fGameDialogRatio    = g_SoundCVars.g_fGameDialogVolume;
	m_fMovieFadeoutVolume = 1.0f;

	m_nReverbType					= -1;
	m_nFadeOutInMS				= 0;
	m_bSoundSystemPause		=	false;

	assert (gEnv->pSystem);
	m_pStreamEngine = gEnv->pSystem->GetStreamEngine();

	m_nLastInactiveSoundsIndex = 0;
	m_nLastPrecacheIndex = 0;
	m_nBuffersLoaded					= 0;

	// Weather related
	m_fWeatherTemperatureInCelsius = 28.0;
	m_fWeatherHumidityAsPercent = 80.0;
	m_fWeatherInversion = 0.0;
	m_fWeatherAirAbsorptionMultiplyer = 1.0;

	m_nMinSoundPriority = 0;
	//m_pDSPUnitSFXFilter=NULL;
	m_fDirAttCone				=	0.0f;
	m_nMuteRefCnt				=	0;
	m_nDialogSoundMoodAlloc = 1;
	m_nDialogEventParameterAlloc = 1;

	 m_DirAttPos = Vec3(0);
	m_DirAttDir = Vec3(0);
	m_fDirAttCone = 1.0f;
	m_fDirAttMaxScale = 0.0f;
	m_bNeedSoundZoomUpdate = false;

	m_bAllowReadRequests = true;

#ifdef INCLUDE_AUDIO_PRODUCTION_CODE
	m_nLoudestSoundId = INVALID_SOUNDID;
	m_pHDRDebugHistory = NULL;
	m_aLoadRequestInfos.reserve(32);
#endif // INCLUDE_AUDIO_PRODUCTION_CODE

	m_fLoudness = 0.0f;

	// profiling
	m_nMaxUsedIDs = 0;

  m_nEaxStatus				=	0;
  m_bNotCalledYetEAX	= 0;

	// scale stuff
	m_fSoundScale = 1.0f;
	
	//m_VisAreas.Clear();
	//memset(m_VisAreas,0,MAX_VIS_AREAS*sizeof(int));
	
	m_DelayedPrecaches.clear();

	m_nSampleRate = g_nFormatSampleRate;

	uint32 dwMaxIndex = (uint32)(m_SoundSaltBuffer.GetTSize());
	m_AllSoundsArray.resize(dwMaxIndex);

	m_SoundTempVisAreas.PreAllocate(MAX_VIS_AREAS);

	//m_InactiveSounds.reserve(50);
	//m_LoopedSounds.reserve(100);
	//m_OneShotSounds.reserve(100);
	//m_stoppedSoundToBeDeleted.reserve(50);
	
	//if (!m_pCVARSoundEnable->GetIVal())		
	//	return;

	m_bOK = true;

	m_bUpdateNeededMoodManager = true;

	strcpy(m_szEmptyName,"NONAME"); // TODO clean up

	m_bResetVolume		=	false;
	m_fSFXResetVolume	=	1.0f;

	m_pSoundAssetManager = (ISoundAssetManager*) new CSoundAssetManager(this);
	
	m_pDebugLogger = (CDebugLogger*) new CDebugLogger("Sound.log");
// MUST set this data before init

  ptParamBOOL DopplerStatus((g_nDoppler ? true : false));// ***** I need bool system gives int
  m_pAudioDevice->SetParam(adpDOPPLER, &DopplerStatus);

  ptParamINT32 mixingRate(g_nFormatSampleRate);
  m_pAudioDevice->SetParam(adpSYSTEM_FREQUENCY , &mixingRate);

	//m_nSpeakerConfig = m_pCVarSpeakerConfig->GetIVal();

  ptParamINT32 speakertype(g_nSpeakerConfig);
  m_pAudioDevice->SetParam(adpSPEAKER_MODE, &speakertype);

	AudioDeviceSettings InitSettings;
	m_pAudioDevice->GetInitSettings(&InitSettings);

	InitSettings.nHWChannels       = g_nHWChannels;
	InitSettings.nSoftwareChannels = g_nSoftwareChannels;
	InitSettings.nSpeakerConfig    = g_nSpeakerConfig;

	//SOUND_MAX_SOFTWARE_CHANNELS;

	m_pAudioDevice->SetInitSettings(&InitSettings);

	m_dwMainThreadId = CryGetCurrentThreadId();

	// TODO: replace hard coded dialog project with cvar
	m_oDialogBufferCRC = "sounds/dialog:dialog";

	m_SoundNameCacheCRC.reserve(512);
}

//////////////////////////////////////////////////////////////////////////
bool CSoundSystem::Init()
{
	// Constructor deals with an UNINITIALIZED AudioDevice
	// Init() can rely on an initialized AudioDevice

	// Sets master volume to highest level.
	SetMasterVolume(1.0f);

	// this is for Craig
	//////////////////////////////////////////////////////////////////////////
	//CMicrophoneStream MicStream;
	//IMicrophoneStream *pMicStream = &MicStream;
	//CMicrophone Mic(pMicStream, m_pAudioDevice);
	//Mic.Record(0, 16,44100,44100);
	//Mic.Stop();
	//Mic.Release();
	//////////////////////////////////////////////////////////////////////////

	string sEventName;

	//Precache("Sounds/weapons:scar:select", 0, sEventName);
		//Precache("Sounds/error:error", 0, sEventName);

	// first load a event then parse all categories
	POOL_NEW(CMoodManager, m_pMoodManager)();

	if (!m_pMoodManager)
		return false;
	
	gEnv->pPhysicalWorld->AddEventClient(EventPhysRWIResult::id, &OnObstructionTest, 1);
	gEnv->pSystem->GetISystemEventDispatcher()->RegisterListener(this);

	// Languages hack, makes the project loads first
	//Precache("sounds/dialog:human", 0, FLAG_SOUND_PRECACHE_LOAD_SOUND|FLAG_SOUND_PRECACHE_LOAD_GROUP|FLAG_SOUND_PRECACHE_STAY_IN_MEMORY);

	
	//Precache("island/hawker_island_ab1_01000", FLAG_SOUND_VOICE, FLAG_SOUND_PRECACHE_LOAD_SOUND|FLAG_SOUND_PRECACHE_LOAD_GROUP|FLAG_SOUND_PRECACHE_STAY_IN_MEMORY|FLAG_SOUND_PRECACHE_UNLOAD_AFTER_PLAY);

	//ISound* pSound = CreateSound("game_over", 0);
	//pSound->Play();

	// debug a crash
	
	//ISound *pSound = NULL;
	//tSoundID m_sounds[5000];

	//while (!pSound)
	//{
	//	float fRand = (float)rand()/(float)RAND_MAX;
	//	float fTest = fRand*65000.0f*65000.0f;
	//	tSoundID nNotInitialized = (tSoundID) fTest;
	//	pSound = GetSound(nNotInitialized);
	//}

	//if (pSound)
	//	int a = 1;

	// call Update once so some member variables are set from cvars and reverb is build
	Update(eSoundUpdateMode_All);

	return true;
}

//////////////////////////////////////////////////////////////////////////
void CSoundSystem::PostInit()
{
	if (m_pFileCacheManager)
	{
		m_pFileCacheManager->Init();
	}

	gEnv->pEntitySystem->GetAreaManager()->AddEventListener(this);
}

//////////////////////////////////////////////////////////////////////////
void CSoundSystem::Log( const ESoundLogType eType, const char *szFormat,... )
{
#ifdef INCLUDE_AUDIO_PRODUCTION_CODE
	if (szFormat && szFormat[0] && gEnv->pLog->GetVerbosityLevel() != -1)
	{
		FRAME_PROFILER("CSoundSystem::Log", GetISystem(), PROFILE_SOUND);

		char sBuffer[256];
		va_list	ArgList;
		va_start(ArgList, szFormat);
		int const nCount = vsnprintf_s(sBuffer, sizeof(sBuffer), sizeof(sBuffer) - 1, szFormat, ArgList);
		sBuffer[sizeof(sBuffer)-1] = '\0';
		va_end(ArgList);

		bool const bBlock  = g_fSpamFilterTimeout > 0 && m_SpamFilter.IsSpam(sBuffer);
		bool const bInsert = g_fSpamFilterTimeout > 0 && !bBlock;

		if (bInsert && eType != eSLT_Always)
		{
			m_SpamFilter.Insert(sBuffer, g_fSpamFilterTimeout);
		}

		switch(eType)
		{
		case eSLT_Message:
			{
				if (!bBlock)
				{
					gEnv->pLog->Log("%s", sBuffer);
				}

				break;
			}
		case eSLT_Warning:
			{
				if (!bBlock)
				{
					gEnv->pSystem->Warning(VALIDATOR_MODULE_SOUNDSYSTEM, VALIDATOR_WARNING, VALIDATOR_FLAG_SOUND, 0, "%s", sBuffer);
				}

				break;
			}
		case eSLT_Error:
			{
				if (!bBlock)
				{
					gEnv->pSystem->Warning(VALIDATOR_MODULE_SOUNDSYSTEM, VALIDATOR_ERROR, VALIDATOR_FLAG_SOUND, 0, "%s", sBuffer);
				}

				break;
			}
		case eSLT_Always:
			{
				CryLogAlways("%s", sBuffer);

				break;
			}
		default:
			{
				assert(0);

				break;
			}
		}
	}

#endif // INCLUDE_AUDIO_PRODUCTION_CODE
}

EPrecacheResult CSoundSystem::Precache( const char *sGroupAndSoundName, uint32 nSoundFlags, uint32 nPrecacheFlags)
{
	if (!g_nPrecacheData)
		return ePrecacheResult_Disabled;

	if (nPrecacheFlags & FLAG_SOUND_PRECACHE_STAY_IN_MEMORY)
	{
		int a = 0;	(void)a;
	}

	CSoundBuffer* pBuf = NULL;
	if (sGroupAndSoundName && sGroupAndSoundName[0])
	{
		char cGroupName[MAX_GROUPPATHLENGTH]	= {'\0'};
		char cEventName[MAX_EVENTNAMELENGTH]	= {'\0'};

		CCryNameCRC oBufferCRC;
		if (GetGroupAndEventNameFast(sGroupAndSoundName, cGroupName, cEventName) != PSNRC_KEY)
		{
			oBufferCRC = cGroupName;
		}
		else
		{
			stack_string sFullVoicePath;
			GetFullVoicePathFast(sGroupAndSoundName, sFullVoicePath); // Optimize for one time mission hints

			if (!sFullVoicePath.empty())
				oBufferCRC = sFullVoicePath;
		}

		pBuf = m_pSoundAssetManager->GetSoundBuffer(oBufferCRC);
		if (!pBuf)
		{
			ParseNameIntoCache(sGroupAndSoundName);

			if (m_oParseNameCache.nReturnFlags == PSNRC_WAV || m_oParseNameCache.nReturnFlags == PSNRC_KEY)
			{
				oBufferCRC = m_oParseNameCache.sFileName.c_str();
				pBuf = m_pSoundAssetManager->GetSoundBuffer(oBufferCRC);
			}

			if (!pBuf && m_oParseNameCache.nReturnFlags)
				pBuf = PrecacheSound(NULL, nSoundFlags, nPrecacheFlags);
		}
	}

	if (pBuf)
	{
		pBuf->GetProps()->nFlags					= nSoundFlags;
		pBuf->GetProps()->nPrecacheFlags	= nPrecacheFlags;

		if ((nPrecacheFlags & FLAG_SOUND_PRECACHE_UNLOAD_NOW) == 0)
			pBuf->Load(NULL);
	}

	if (m_bDelayPrecaching)
		return ePrecacheResult_Delayed;

	if (pBuf != NULL)
		return ePrecacheResult_OK;

	if (GetError() & eSoundSystemErrorCode_ProjectNotReady)
		return ePrecacheResult_ProjectNotReady;

	return ePrecacheResult_Error;
}

//////////////////////////////////////////////////////////////////////////
EPrecacheResult const CSoundSystem::PrecacheDialogLine(char const* const sKeyName)
{
	if (sKeyName && sKeyName[0])
	{
		// First check for the dialog project buffer.
		CSoundBuffer* pSoundBuffer = m_pSoundAssetManager->GetSoundBuffer(m_oDialogBufferCRC);

		if (!pSoundBuffer)
		{
			SSoundBufferProps oDialogBufferProps;
			oDialogBufferProps.sName          = "sounds/dialog:dialog";
			oDialogBufferProps.sUserDataText  = "dialog";
			oDialogBufferProps.nFlags         = FLAG_SOUND_EVENT;
			oDialogBufferProps.nPrecacheFlags = FLAG_SOUND_PRECACHE_LOAD_SOUND;
			oDialogBufferProps.eBufferType    = btEVENT;

			pSoundBuffer = m_pSoundAssetManager->CreateSoundBuffer(oDialogBufferProps, m_oDialogBufferCRC);
		}

		if (pSoundBuffer)
		{
			// Now make sure the actual dialog project is loaded.
			if (pSoundBuffer->GetProps()->nProjectIndex != PROJECTINDEX_INVALID)
			{
#if defined(SOUNDSYSTEM_USE_FMODEX400)
				SProjectFile const* const pProjectFile = m_pAudioDevice->GetProjectFile(pSoundBuffer->GetProps()->nProjectIndex, false);

				if (!pProjectFile)
#endif
				{
					// This case needs investigation!
					assert(0);
					pSoundBuffer->GetProps()->nProjectIndex = m_pAudioDevice->LoadProjectFile("sounds/dialog/", "dialog.fev", true);
				}
			}
			else
			{
				pSoundBuffer->GetProps()->nProjectIndex = m_pAudioDevice->LoadProjectFile("sounds/dialog/", "dialog.fev", true);
			}

			// Now precache the buffer for the actual dialog line.
			stack_string sFullVoicePath;
			GetFullVoicePathFast(sKeyName, sFullVoicePath);
			CCryNameCRC const oBufferCRC(sFullVoicePath.c_str());
			pSoundBuffer = m_pSoundAssetManager->GetSoundBuffer(oBufferCRC);

			if (!pSoundBuffer)
			{
				ParseNameIntoCache(sKeyName);
				pSoundBuffer = PrecacheSound(NULL, FLAG_SOUND_VOICE, FLAG_SOUND_PRECACHE_DIALOG_DEFAULT);
				assert(pSoundBuffer != NULL);

				if (pSoundBuffer)
				{
					bool const bSuccess = pSoundBuffer->Load(NULL);
					assert(bSuccess);
					assert(pSoundBuffer->Loading() || pSoundBuffer->Loaded(false));
				}
			}

			return (pSoundBuffer != NULL) ? ePrecacheResult_OK : ePrecacheResult_Error;
		}
	}

	return ePrecacheResult_Error;
}

// Precaches the sound data of a sound definition
CSoundBuffer* CSoundSystem::PrecacheSound(CSoundBuffer* const pBuf, uint32 nSoundFlags, uint32 nPrecacheFlags)
{
	FUNCTION_PROFILER( gEnv->pSystem, PROFILE_SOUND );

	if (!m_bSoundSystemEnabled) 
		return (NULL);

	SSoundBufferProps oTempBufferProps;
	CSoundBuffer* pNewBuf			= pBuf;
	TFixedResourceName sTemp	= m_oParseNameCache.sFileName;
	oTempBufferProps.nFlags		= nSoundFlags & SOUNDBUFFER_FLAG_MASK;

	bool bSysPreload = false;
	//GDC2008 console hack, remove AND with global preload
	//ICVar *pSysPreload = gEnv->pConsole->GetCVar( "sys_preload" );
	//if (pSysPreload && pSysPreload->GetIVal() != 0)
	bSysPreload = true;

	// Load data if precaching and sysPreload is activated
	oTempBufferProps.nPrecacheFlags = (g_nPrecacheData && bSysPreload) ? nPrecacheFlags : (nPrecacheFlags & ~FLAG_SOUND_PRECACHE_LOAD_GROUP);

	if (m_oParseNameCache.nReturnFlags & PSNRC_EVENT || m_oParseNameCache.nReturnFlags & PSNRC_GROUP)
	{
		oTempBufferProps.eBufferType = btEVENT;

		const char *pProjectPath = m_oParseNameCache.sProjectPath;
		oTempBufferProps.sName.assign(pProjectPath, m_oParseNameCache.sProjectPath.length()-1); // skip the last slash
		oTempBufferProps.sName += ":";
		oTempBufferProps.sName += m_oParseNameCache.sGroupName;
		oTempBufferProps.sUserDataText = m_oParseNameCache.sGroupName.c_str();

		// Check if Project is loaded
		m_oParseNameCache.sProjectName += ".fev";

		bool bAsyncLoading = 	!(nSoundFlags & FLAG_SOUND_LOAD_SYNCHRONOUSLY) && !(nPrecacheFlags & FLAG_SOUND_PRECACHE_LOAD_PROJECT) && g_StreamProjectFiles;
		oTempBufferProps.nProjectIndex = m_pAudioDevice->LoadProjectFile(m_oParseNameCache.sProjectPath.c_str(), m_oParseNameCache.sProjectName.c_str(), bAsyncLoading);

	}
	else
	{
		oTempBufferProps.eBufferType	= btSAMPLE;
		oTempBufferProps.sName				= m_oParseNameCache.sFileName;

		bool bFoundFile = false;

		// check if suggested compression is available
		int nPos = m_oParseNameCache.sFileName.length() - 4;
		if (nPos > 0)
		{
			int nTryThisCompression = g_nCompression ? g_nCompression : g_nInternCompression;

			if (nTryThisCompression == 1 && !bFoundFile)
			{
				m_oParseNameCache.sFileName.replace(nPos, 4, ".fsb");
				uint32 nSize = gEnv->pCryPak->FGetSize(m_oParseNameCache.sFileName.c_str());
				bFoundFile = (nSize > 0);
				nTryThisCompression = 4;
			}

			if (nTryThisCompression == 4 && !bFoundFile)
			{
				m_oParseNameCache.sFileName.replace(nPos, 4, ".xma");
				uint32 nSize = gEnv->pCryPak->FGetSize(m_oParseNameCache.sFileName.c_str());
				bFoundFile = (nSize > 0);
				nTryThisCompression = 2;
			}

			if (nTryThisCompression == 2 && !bFoundFile)
			{
				m_oParseNameCache.sFileName.replace(nPos, 4, ".mp2");
				uint32 nSize = gEnv->pCryPak->FGetSize(m_oParseNameCache.sFileName.c_str());
				bFoundFile = (nSize > 0);
				nTryThisCompression = 3;
			}

			if (nTryThisCompression == 3 && !bFoundFile)
			{
				m_oParseNameCache.sFileName.replace(nPos, 4, ".mp3");
				uint32 nSize = gEnv->pCryPak->FGetSize(m_oParseNameCache.sFileName.c_str());
				bFoundFile = (nSize > 0);
				nTryThisCompression = 5;
			}

			if (nTryThisCompression == 5 && !bFoundFile)
			{
				m_oParseNameCache.sFileName.replace(nPos, 4, ".wav");
				uint32 nSize = gEnv->pCryPak->FGetSize(m_oParseNameCache.sFileName.c_str());
				bFoundFile = (nSize > 0);

				if (!bFoundFile)
				{
					// restore original file name
					m_oParseNameCache.sFileName = sTemp;
					nSize = gEnv->pCryPak->FGetSize(m_oParseNameCache.sFileName.c_str());
					bFoundFile = (nSize > 0);
				}
			}
		}

		if (!bFoundFile)
			return NULL;

		oTempBufferProps.sName = m_oParseNameCache.sFileName;
	}

	if (m_oParseNameCache.nReturnFlags & PSNRC_VOICE || nSoundFlags & FLAG_SOUND_VOICE)
		oTempBufferProps.nFlags |= FLAG_SOUND_VOICE;

	if (!pNewBuf)
	{
		CCryNameCRC const oBufferCRC(oTempBufferProps.sName);
		pNewBuf = m_pSoundAssetManager->CreateSoundBuffer(oTempBufferProps, oBufferCRC);

		if (pNewBuf)
		{
			if (m_bDelayPrecaching)
				m_DelayedPrecaches[oTempBufferProps] = oBufferCRC;
			else
			{
				if (oTempBufferProps.nProjectIndex != PROJECTINDEX_INVALID)
				{
					pNewBuf->Load(NULL);
				}
			}
		}

		if (!pNewBuf || !pNewBuf->Loading() && pNewBuf->LoadFailure() && !g_nErrorSound)
			return NULL;
	}
	else
	{
		pNewBuf->GetProps()->nFlags					= oTempBufferProps.nFlags;
		pNewBuf->GetProps()->nPrecacheFlags	= oTempBufferProps.nPrecacheFlags;
		pNewBuf->GetProps()->nProjectIndex	= oTempBufferProps.nProjectIndex;
	}

	return pNewBuf;  
}

ISound* CSoundSystem::GetNewSoundObject()
{
	FRAME_PROFILER("CSoundSystem::GetNewSoundObject", GetISystem(), PROFILE_SOUND);

	CSound* pSound = NULL;
	
	if (m_UnusedSoundObjects.empty())
	{
		POOL_NEW(CSound, pSound)(this);
		return pSound;
	}
	else
	{
		pSound = (CSound* const)(m_UnusedSoundObjects[m_UnusedSoundObjects.size()-1]);
		m_UnusedSoundObjects.pop_back();
	}

	return pSound;
}

//////////////////////////////////////////////////////////////////////////
bool CSoundSystem::RemoveSoundObject(ISound* pSound)
{
	if (pSound)
	{
#ifdef INCLUDE_AUDIO_PRODUCTION_CODE
		if (stl::find(m_UnusedSoundObjects, pSound))
			__debugbreak();
#endif // INCLUDE_AUDIO_PRODUCTION_CODE

		CSound* const pCSound = (CSound* const)(pSound);
		pCSound->Reset();

		int const nCount = static_cast<int>(m_UnusedSoundObjects.size());

		if (nCount < g_nUnusedSoundCount)
		{
			m_UnusedSoundObjects.push_back(pSound);
		}
		else
		{
			// Don't delete just yet, defer to the next update.
			m_ToBeDeletedSoundObjects.push_back(pCSound);
		}
	}

	return true;
}

ESoundSystemErrorCode const CSoundSystem::GetCullingByCache ( const uint32 nGroupAndSoundNameCRC, const Vec3& vPosition, SSoundCacheInfo &CullInfo )
{
	CullInfo.bCanBeCulled = false;

	if (g_nCullingByCache)
	{
		float fDistanceMultiplier = 1.0f;

		if ((m_fDirAttCone > 0.0f) && (m_fDirAttCone < 1.0f))
		{
			Vec3 DirToPlayer = vPosition - m_DirAttPos;
			DirToPlayer.Normalize();
			float fScale = ((1.0f - (DirToPlayer * m_DirAttDir) ) * __fres(1.0f - m_fDirAttCone));

			if ((fScale > 0.0f) && (fScale <= 1.0f))	// inside cone
			{
				m_fDirAttMaxScale = max((1.0f - fScale), m_fDirAttMaxScale);
				fDistanceMultiplier = __fres(fScale);
			}
		}

		tMapSoundNameCacheCRC::const_iterator ItFindCRC = m_SoundNameCacheCRC.find(nGroupAndSoundNameCRC);

		if (ItFindCRC != m_SoundNameCacheCRC.end())
		{
			// Found Entry, so use it
			SSoundCacheInfoInternal const* const pEntry = &(*ItFindCRC).second;

			CullInfo.b2D = pEntry->b2D;
			CullInfo.bLooping = pEntry->bLooping;
			CullInfo.fMaxRadiusSq = pEntry->fMaxRadiusSq * fDistanceMultiplier * fDistanceMultiplier; // squared so multiply twice

			if (pEntry->b2D || pEntry->bLooping)
			{
				CullInfo.bCanBeCulled = false;
				return eSoundSystemErrorCode_None;
			}

			ListenerID const nListenerID			= GetClosestActiveListener(vPosition);
			IListener const* const pListener	= GetListener(nListenerID);

			if (pListener)
			{
				Vec3 const vListenerPos(pListener->GetPosition());
				float const fDistanceSq = vListenerPos.GetSquaredDistance(vPosition);

				if (fDistanceSq > CullInfo.fMaxRadiusSq)
					CullInfo.bCanBeCulled = true;
			}
			return eSoundSystemErrorCode_None;
		}
	}

	return eSoundSystemErrorCode_SoundCRCNotFoundInCache;
}

// quick test for early culling
ESoundSystemErrorCode const CSoundSystem::GetCullingByCache ( char const* const sGroupAndSoundName, const Vec3& vPosition, SSoundCacheInfo &CullInfo )
{
	const CCryNameCRC cacheNameCRC(sGroupAndSoundName);
	return GetCullingByCache(cacheNameCRC.get(), vPosition, CullInfo);
}

void CSoundSystem::HackFixupName(const char *soundName, char *output_buffer, size_t output_buffer_size)
{
	if (gEnv->bMultiplayer && g_SoundCVars.g_autoMpSoundNameFixup)
	{
		CryFixedStringT<512> soundNameCopy = soundName;

		const char *slash = soundName + 6;

		// This is a weapon sound
		if (slash && *slash == '/' && *(slash+1) == 'w' && *(slash+2) == '_')
		{
			bool is_attachment = *(slash+3) == 'a' && *(slash+4) == 't' && *(slash+5) == 't';
			if (!is_attachment)
			{
				const char *colon = strchr(slash, ':');

				if (colon && *(colon-3) != '_' && *(colon-2) != 'm' && *(colon-1) != 'p')
				{
					// This is not marked as an MP project
					soundNameCopy.insert(colon - soundName, "_mp");
				}
			}
		}

		strncpy(output_buffer, soundNameCopy.c_str(), output_buffer_size);
		output_buffer[output_buffer_size-1] = '\0';

		return;
	}

	strncpy(output_buffer, soundName, output_buffer_size);
	output_buffer[output_buffer_size-1] = '\0';
}

ISound* const CSoundSystem::CreateSound(char const* const sGroupAndSoundName, uint32 nFlags, uint32 nFlagsExtended /* = 0 */, uint32 const nPrecacheFlags /* = 0 */)
{
	ISound *pSound = NULL;

	// dont call this from other than Main thread
	if (!IsMainThread())
	{
		Log(eSLT_Error, "<Sound> %s Called CreateSound from other than Main thread", sGroupAndSoundName );

//		int nCreateSoundCalledFromNonMainThread = 0;
//		assert(nCreateSoundCalledFromNonMainThread);

		m_ErrorCode = (ESoundSystemErrorCode) (m_ErrorCode | eSoundSystemErrorCode_NotCalledFromMainThread);
		return pSound;
	}

	if (nFlags == 0 || nFlags == 4)
	{
		int nCatchme = 1;	(void)nCatchme;
	}

	//if no Sound.pak is present, this will crash on any platform
#if !defined(PS3) || defined(SOUNDSYSTEM_USE_FMODEX400)
	pSound = LoadSound(sGroupAndSoundName, nFlags, nFlagsExtended, nPrecacheFlags | FLAG_SOUND_PRECACHE_LOAD_SOUND);
#endif
	return pSound;
}

ISound* const CSoundSystem::CreateLineSound(char const* const sGroupAndSoundName, uint32 nFlags, uint32 nFlagsExtended, const Vec3 &vStart, const Vec3 &VEnd)
{
	if (!vStart.IsValid() || !VEnd.IsValid())
	{
		Log(eSLT_Warning, "<Sound> %s Invalid input values (possible NaNs) on CreateLineSound()", sGroupAndSoundName );
		assert(0);
		return false;
	}
	
	ISound *pSound = NULL;

#if !defined(PS3) || defined(SOUNDSYSTEM_USE_FMODEX400)
	pSound = LoadSound(sGroupAndSoundName, nFlags, nFlagsExtended, FLAG_SOUND_PRECACHE_LOAD_SOUND);
	if (pSound)
		pSound->SetLineSpec(vStart, VEnd);
#endif
	return pSound;
}

ISound* const CSoundSystem::CreateSphereSound(char const* const sGroupAndSoundName, uint32 nFlags, uint32 nFlagsExtended, const float fRadius)
{
	if (!NumberValid(fRadius))
	{
		Log(eSLT_Warning, "<Sound> %s Invalid input values (possible NaNs) on CreateSphereSound()", sGroupAndSoundName);
		assert(0);
		return false;
	}

	ISound *pSound = NULL;

#if !defined(PS3) || defined(SOUNDSYSTEM_USE_FMODEX400)
	pSound = LoadSound(sGroupAndSoundName, nFlags, nFlagsExtended, FLAG_SOUND_PRECACHE_LOAD_SOUND);
	if (pSound)
		pSound->SetSphereSpec(fRadius);
#endif
	return pSound;
}

//////////////////////////////////////////////////////////////////////////
tSoundID CSoundSystem::CreateSoundID() // TODO change to Entity ID System
{
	tSoundID SoundID = HandleToId(m_SoundSaltBuffer.InsertDynamic());	// get entity id and mark it

	if(!SoundID)
	{
		//Log(eSLT_Error, "<Sound> Create SoundID failed, ID range is full (internal error)");
		return INVALID_SOUNDID;
	}

	m_currentSoundIDs.insert(SoundID);

	//Log(eSLT_Always, "Sound ID destroyed: %d\n", SoundID);

	return SoundID;
}

//////////////////////////////////////////////////////////////////////////
bool CSoundSystem::DestroySoundID(tSoundID SoundID)
{
	//Log(eSLT_Always, "Sound ID destroyed: %d\n", SoundID);
	m_SoundSaltBuffer.Remove(IdToHandle(SoundID));
	m_currentSoundIDs.erase(SoundID);

	return true;
}

//////////////////////////////////////////////////////////////////////////
void CSoundSystem::ShutDown()
{
	Reset(true);

	if (m_pMoodManager)
		m_pMoodManager->ShutDown();

	if (m_pSoundAssetManager)
		m_pSoundAssetManager->ShutDown();

	if (m_pReverbManager)
		m_pReverbManager->Release();

	if (m_bOK)
		m_pAudioDevice->ShutDownDevice();
}

//////////////////////////////////////////////////////////////////////
void CSoundSystem::Reset(bool bUnload)
{
	if (bUnload)
	{
		// reset listener vis areas - just to be 100% sure we dont use an invalid vis area
		RecomputeSoundOcclusion(false, false, true);

		// group stuff
		// reset sound-scaling
		m_fSoundScale = 1.0f;

		// stop all sounds
		Silence(true, false);

		// quick test, sets all Sys-Ptrs in Sounds to NULL
		//CSound* pSound = new CSound(NULL, 0, NULL);

		// for the sake of clarity...
		stl::free_container(m_OneShotSounds);
		stl::free_container(m_LoopedSounds);
		stl::free_container(m_stoppedSoundToBeDeleted);
		stl::free_container(m_InactiveSounds);
		
		m_nLastInactiveSoundsIndex = 0;
		m_nLastPrecacheIndex = 0;

		std::fill(m_AllSoundsArray.begin(), m_AllSoundsArray.end(), static_cast<CSound*>(NULL));

		SoundVecIter IterEnd = m_AllSoundsArray.end();
		for (SoundVecIter Iter = m_AllSoundsArray.begin(); Iter!=IterEnd; ++Iter)
			(*Iter) = NULL;

		m_SpamFilter.Reset();
		if(m_pSoundAssetManager)
			m_pSoundAssetManager->Reset();

		// unload all data
		Silence(true, true);

		stl::free_container(m_ParameterNames);

		assert(m_EventListeners.size() == 0);
		stl::free_container(m_EventListeners);
		stl::free_container(m_EventListenersTemp);

		for (SoundVecIter it = m_ToBeDeletedSoundObjects.begin(), itEnd = m_ToBeDeletedSoundObjects.end(); it != itEnd; ++ it)
			delete (CSound* const)(*it);

		stl::free_container(m_ToBeDeletedSoundObjects);

		for (SoundVecIter it = m_UnusedSoundObjects.begin(), itEnd = m_UnusedSoundObjects.end(); it != itEnd; ++ it)
			delete (CSound* const)(*it);

		stl::free_container(m_UnusedSoundObjects);
		if(m_pAudioDevice)
			m_pAudioDevice->Reset(false);

		if (m_pReverbManager)
		{
			m_pReverbManager->Reset();
			delete m_pReverbManager;
			m_pReverbManager = NULL;
		}

//		if (m_DelayedPrecaches.size()	!= 0)
//		{
//			__debugbreak();
//		}

		m_DelayedPrecaches.clearAndFreeMemory();

#ifdef INCLUDE_AUDIO_PRODUCTION_CODE
		stl::free_container(m_aLoadRequestInfos);
#endif // INCLUDE_AUDIO_PRODUCTION_CODE
	}
	else
	{
		m_ToBeDeletedSoundObjects.reserve(128);

		// prepare a user specified count of CSoundObjects and store them in the m_UnusedSoundObjects container
		if (m_UnusedSoundObjects.empty())
		{
			m_UnusedSoundObjects.reserve(g_nUnusedSoundCount);

			for (int i = 0; i < g_nUnusedSoundCount; ++i)
			{
				POOL_NEW_CREATE(CSound, pSound)(this);

				if (pSound)
					m_UnusedSoundObjects.push_back(pSound);
			}
		}

		m_pAudioDevice->Reset(true);
	}
}

//////////////////////////////////////////////////////////////////////
CSoundSystem::~CSoundSystem()
{
	GUARD_HEAP;

	gEnv->pPhysicalWorld->RemoveEventClient(EventPhysRWIResult::id, &OnObstructionTest, 1);
	gEnv->pSystem->GetISystemEventDispatcher()->RemoveListener(this);

	// Needed for when the engine is initialized with the NULLSoundSystem.
	// This instance then gets released before the EntitySystem has been initialized.
	if (gEnv->pEntitySystem)
	{
		gEnv->pEntitySystem->GetAreaManager()->RemoveEventListener(this);
	}

	ShutDown();

	ISoundProfileInfo *pSoundInfo = NULL;
	tmapSoundInfo::iterator ItEnd = m_SoundInfos.end();
	for (tmapSoundInfo::const_iterator It = m_SoundInfos.begin(); It!=ItEnd; ++It)
	{
		pSoundInfo = (*It).second;
		delete pSoundInfo;
	}

	int nEnd = m_UnusedSoundObjects.size();
	for (int i=0; i<nEnd; ++i)
		delete m_UnusedSoundObjects[i];

	m_bOK = false;
	delete m_pDebugLogger;
	m_pDebugLogger = NULL;
	delete m_pSoundAssetManager;
	m_pSoundAssetManager = NULL;

	if (m_pReverbManager)
	{
		delete m_pReverbManager;
		m_pReverbManager = NULL;
	}

	delete m_pMoodManager;
	m_pMoodManager = NULL;
	delete m_pAudioDevice;
	m_pAudioDevice = NULL;

	if (m_pFileCacheManager)
		POOL_FREE(m_pFileCacheManager);

	// Unregister audio cvars
	g_SoundCVars.UnregisterVariables();

	//m_VisAreas.Clear();


	//SetEaxListenerEnvironment(EAX_PRESET_OFF);

	// Freeing Memory Pool Memory again
	uint8* pMemSystem = g_MemoryPoolPrimary.Data();
	g_MemoryPoolPrimary.UnInitMem();
	if (pMemSystem)
		delete[] (uint8*)(pMemSystem);
#ifdef PROVIDE_SECONDARY_POOL		
	uint8* pMemSecondary = g_MemoryPoolSoundSecondary.Data();
	if (pMemSecondary)
	{





		delete[] (uint8*)(pMemSecondary);

	}
#endif
}

//////////////////////////////////////////////////////////////////////
void CSoundSystem::Release()
{
	delete this;
}

//////////////////////////////////////////////////////////////////////////
void CSoundSystem::DeactivateSound( ISound *pSound )
{
	CSound* const pCSound = (CSound* const)(pSound);

	if (pCSound->IsPlaying())
		pCSound->Stop();

	if (pCSound->GetState() == eSoundState_Active)
	{
		if (pCSound->GetLoopMode()) 
		{
			m_LoopedSounds.EraseUnique(pSound);
			m_InactiveSounds.AddUnique(pSound);
		}
		else
		{
			// OneShot Sounds are not moved to inactive
			m_OneShotSounds.EraseUnique(pSound);
		}

		pCSound->SetState(eSoundState_Inactive);
//		m_setSoundSpotsInactive.insert(pSound);

	}
}

//////////////////////////////////////////////////////////////////////////
bool CSoundSystem::ProcessActiveSound( ISound *pSound )
{
	//FUNCTION_PROFILER( GetSystem(),PROFILE_SOUND );

	bool bFadeoutFinished = false;
	float fRatio = 1.0f;

	if (pSound->GetFlags() & FLAG_SOUND_RADIUS)
	{
		float fDist2 = GetSqDistanceToClosestListener(pSound);
  
		CSound* const pCSound = (CSound* const)(pSound);
		if (fDist2 > pCSound->GetSquardedMaxRadius())
			return false; // Too far

		if (bFadeoutFinished)
			return false;

		//calc attenuation, cal sound ratio between min & max radius
		fRatio = pCSound->GetRatioByDistance(sqrt_tpl(fDist2), false);
	}
	(void)fRatio;
	// if Steve doesn't want to change frequency and pan, this can be replaced just with set volume
	//pSound->SetRatio(fRatio*cs->GetCurrentFade()*cs->GetRatio());
	return true;
}

bool CSoundSystem::ProcessReverb()
{
	assert(m_pReverbManager);
	assert (m_pMoodManager);

	if(!gEnv->pGame)
		return false;
	
	IEntity const* const pPlayerEntity = gEnv->pEntitySystem->GetEntity(gEnv->pGame->GetIGameFramework()->GetClientActorId());

	if (pPlayerEntity)
	{
		CListener const* const pListener = (CListener*)GetListener(GetClosestActiveListener(pPlayerEntity->GetWorldPos()));

		if (pListener)
		{
			float fDepth = pListener->GetUnderwater();

			if (fDepth < 0.0f)
			{
				float fMoodFade = -fDepth/25.0f + 0.6f;
				fMoodFade = min(1.0f, fMoodFade);

				if (m_pMoodManager)
				{
					m_pMoodManager->RegisterSoundMood("underwater");

					if (m_pMoodManager->GetSoundMoodFade("underwater") != fMoodFade)
						m_pMoodManager->UpdateSoundMood("underwater", fMoodFade, 100);
				}

				if (m_nReverbType > 0)
				{
					m_pReverbManager->SetListenerReverb(REVERB_PRESET_UNDERWATER, 0, 0);
					m_pReverbManager->Update(pListener->bInside);
				}
			}
			else
			{
				// still updating?
				if (m_pMoodManager)
				{
					if (m_pMoodManager->GetSoundMoodFade("underwater") > 0.0f)
						m_pMoodManager->UpdateSoundMood("underwater", 0.0f, 100);

					if (m_pMoodManager->GetSoundMoodFade("underwater") == 0.0f)
						m_pMoodManager->UnregisterSoundMood("underwater");
				}

				if (m_nReverbType > 0)
				{
					m_pReverbManager->SetListenerReverb(REVERB_PRESET_OFF, 0, 0);
					m_pReverbManager->Update(pListener->bInside);
				}
			}

			return true;
		}
	}
	
	return false;
}

//returns and resets music system update time
float CSoundSystem::GetUpdateMilliseconds()
{
	float fTimeMS = SUpdateTimer::ConvertToMS(m_UpdateTicks);
	m_UpdateTicks = 0;
	return fTimeMS;
}

//////////////////////////////////////////////////////////////////////
void CSoundSystem::Update(const ESoundUpdateMode UpdateMode, bool const bIgnoreTimers /* = false */)
{
	GUARD_HEAP;
	FUNCTION_PROFILER( gEnv->pSystem, PROFILE_SOUND );
	UPDATE_TIMER

	float fFrameTime = gEnv->pTimer->GetFrameTime();

	if (UpdateMode & eSoundUpdateMode_Sounds)
	{
		if (g_nHDR == 1)
		{
			ProcessHDRS(fFrameTime); 
		}
	}

	if (UpdateMode & eSoundUpdateMode_Rest)
	{
		if (m_pMoodManager && g_nSoundMoods)
			m_pMoodManager->Update();

		// check if the soundsystem is currently fading out
		if (m_nFadeOutInMS > 0)
		{
			int nDifferenceMS = (int) (gEnv->pTimer->GetRealFrameTime() * 1000);
			float fTimeRatio = 1.0f - (min(nDifferenceMS, (int)m_nFadeOutInMS)) / float(m_nFadeOutInMS);
			
			nDifferenceMS = m_nFadeOutInMS - nDifferenceMS;
			m_nFadeOutInMS = max(0, nDifferenceMS);

			g_SoundCVars.g_fGameDialogVolume *= fTimeRatio;
			g_SoundCVars.g_fGameSFXVolume *= fTimeRatio;


			// we reached the fade out goal. Pause all sounds if SoundSystem is supposed to be paused
			if (m_bSoundSystemPause && m_nFadeOutInMS == 0)
			{
				Pause(true, false, 0);
				g_SoundCVars.g_fGameDialogVolume = 1.0f;
				g_SoundCVars.g_fGameSFXVolume = 1.0f;
			}
		}



		// check if Music volume has changed about a delta
		if ((Ffabs(g_SoundCVars.g_fMusicVolume - m_fMusicVolume) > 0.01f) || (Ffabs(g_SoundCVars.g_fGameMusicVolume - m_fGameMusicVolume) > 0.01f))
		{
			// Setting MIN or MAX values to the Music volume
			g_SoundCVars.g_fMusicVolume = clamp(g_SoundCVars.g_fMusicVolume, 0.0f, 1.0f);
			m_fMusicVolume = g_SoundCVars.g_fMusicVolume;// * m_fMusicEffectVolume;

			g_SoundCVars.g_fGameMusicVolume = clamp(g_SoundCVars.g_fGameMusicVolume, 0.0f, 1.0f);
			m_fGameMusicVolume = g_SoundCVars.g_fGameMusicVolume;
		}

		// check if SFX volume has changed about a delta
		if ( (Ffabs(g_SoundCVars.g_fSFXVolume - m_fSFXVolume) > 0.01f) 
			|| (Ffabs(g_SoundCVars.g_fGameSFXVolume - m_fGameSFXVolume) > 0.01f) 
			|| (Ffabs(g_SoundCVars.g_fGameDialogVolume - m_fGameDialogVolume) > 0.01f)
			|| (Ffabs(g_SoundCVars.g_fDialogVolume - m_fDialogVolume) > 0.01f))
		{
			// Setting MIN or MAX values to the SFX volume
			g_SoundCVars.g_fSFXVolume = clamp(g_SoundCVars.g_fSFXVolume, 0.0f, 1.0f);
			m_fSFXVolume = g_SoundCVars.g_fSFXVolume;		

			g_SoundCVars.g_fGameSFXVolume = clamp(g_SoundCVars.g_fGameSFXVolume, 0.0f, 1.0f);
			m_fGameSFXVolume = g_SoundCVars.g_fGameSFXVolume;		

			g_SoundCVars.g_fGameDialogVolume = clamp(g_SoundCVars.g_fGameDialogVolume, 0.0f, 1.0f);
			m_fGameDialogVolume = g_SoundCVars.g_fGameDialogVolume;	

			g_SoundCVars.g_fDialogVolume = clamp(g_SoundCVars.g_fDialogVolume, 0.0f, 1.0f);
			m_fDialogVolume = g_SoundCVars.g_fDialogVolume;	

			// call set volume for each sound so it can rescale itself		
			// TODO optimize only go through set of registered IDs
			TSoundIDSet::const_iterator ItEnd = m_currentSoundIDs.end();
			for (TSoundIDSet::const_iterator It=m_currentSoundIDs.begin(); It!=ItEnd; ++It)
			{
				CSound* const pSound = (CSound* const)(GetSound(*It));

				if (pSound)
				{
					if (pSound->GetFlags() & FLAG_SOUND_MUSIC)			
						continue;	// this one is a music sound, so skip it		
					pSound->SetVolume(pSound->GetVolume());
				}
			} // for loop
		}

		// Check for ReverbType change in console
#ifdef SOUNDSYSTEM_USE_FMODEX400
		if (!m_pReverbManager || g_SoundCVars.g_nReverbType != m_nReverbType)
		{
			switch (g_SoundCVars.g_nReverbType)
			{
			case REVERB_TYPE_SOFTWARE_HIGH:
			case REVERB_TYPE_SOFTWARE_LOW:
				{
					// The previous type must be different from the new one.
					if (!m_pReverbManager || (m_nReverbType != REVERB_TYPE_SOFTWARE_HIGH && m_nReverbType != REVERB_TYPE_SOFTWARE_LOW))
					{
						// Free previous data if necessary but not if the reverb was only disabled.
						if (m_pReverbManager && m_nReverbType != REVERB_TYPE_NONE)
						{
							m_pReverbManager->Release();
							delete m_pReverbManager;
							m_pReverbManager = NULL;
							m_pSoundAssetManager->UnloadAllSoundBuffers();
						}

						if (!m_pReverbManager)
						{
							m_pReverbManager = new CReverbManagerEAX();
							m_pReverbManager->Init(m_pAudioDevice, this);
						}
						
						m_nReverbType = g_SoundCVars.g_nReverbType;
						m_pReverbManager->SelectReverb(m_nReverbType);
					}

					// Set back to the working value.
					g_SoundCVars.g_nReverbType = m_nReverbType;

					break;
				}
			case REVERB_TYPE_NONE:
				{
					m_nReverbType = g_SoundCVars.g_nReverbType;

					if (m_pReverbManager)
					{
						m_pReverbManager->SelectReverb(m_nReverbType);
					}
					
					break;
				}
			}
		}
#endif // SOUNDSYSTEM_USE_FMODEX400

		m_SpamFilter.Update(fFrameTime);
	}

	if (UpdateMode & eSoundUpdateMode_Listeners)
	{
		// Set listeners first
		m_pAudioDevice->UpdateListeners();
	}

	if (UpdateMode & eSoundUpdateMode_Rest)
	{
		// if sound is disabled in the console always calls Silence()
		if (g_nSoundEnable != 0)
		{
			if (!m_bSoundSystemEnabled)
			{
				m_bSoundSystemEnabled = true;

				if (m_pFileCacheManager)
				{
					m_pFileCacheManager->ChangeCacheStatus(true);
				}
			}
		}
		else
		{
			if (m_bSoundSystemEnabled)
			{
				Silence(true, true);

				if (m_pMoodManager)
				{
					m_pMoodManager->RefreshCategories();
				}

				m_bSoundSystemEnabled = false;

				if (m_pFileCacheManager)
				{
					m_pFileCacheManager->ChangeCacheStatus(false);
				}
			}
			else
			{
				// Draw Audio Information to the screen
				RenderAudioInformation();
				return;
			}
		}
	}

	CTimeValue const tCurrentFrameTime(gEnv->pTimer->GetFrameStartTime());

	if (UpdateMode & eSoundUpdateMode_Rest)
	{

		// checking Indoor/Outdoor and VisAreas
		// reset listener vis areas - just to be 100% sure we dont use an invalid vis area
		RecomputeSoundOcclusion(true, false);
		tVecListenersIter IterEnd = m_vecListeners.end();

		for (tVecListenersIter Iter = m_vecListeners.begin(); Iter != IterEnd; ++Iter)
		{
			CListener* pListener = (*Iter);
			pListener->bInside = false;
			pListener->bInside = (pListener->GetVisArea() != NULL);
		}

		// check if sample rate has changed
		m_nSampleRate = g_nFormatSampleRate;

		// TODO Reactivate
		// Do the reverb calculation, blending of EAX areas
		int64 const nMS	= (tCurrentFrameTime - m_tUpdateReverbManager).GetMilliSecondsAsInt64();

		if (abs(nMS) > UPDATE_SOUND_REVERB_MANAGER_IN_MS && m_bEnteredGame)
		{
			ProcessReverb();
			m_tUpdateReverbManager = tCurrentFrameTime;
		}
	}
 
	//////////////////////////////////////////////////////////////////////////
	// calling Update() for all active sound spots
	if (UpdateMode & eSoundUpdateMode_Sounds || UpdateMode & eSoundUpdateMode_LostFocus)
	{
		// if there is no mission hint play fade it out
		// sound updates will overwrite this sound mood
		ProcessInactiveSounds(UpdateMode);
		ProcessOneShotSounds();
		ProcessLoopedSounds();

		// Here actually delete all auto stop sounds.
		if (!m_stoppedSoundToBeDeleted.empty())
		{
			m_stoppedSoundToBeDeleted.clear();
		}

		m_bNeedSoundZoomUpdate = false;
	}

	if (UpdateMode & eSoundUpdateMode_Rest)
	{
		// Update the AssetManager
		m_pSoundAssetManager->Update(bIgnoreTimers);

		//////////////////////////////////////////////////////////////////////////
		// Update the AudioDevice
		int64 nMS = (tCurrentFrameTime - m_tUpdateAudioDevice).GetMilliSecondsAsInt64();

		if (abs(nMS) > 20 || tCurrentFrameTime == m_tLastFrameTime) // game is paused, no time passed
		{
			m_pAudioDevice->Update();
			m_tUpdateAudioDevice = tCurrentFrameTime;
		}

		// Update the FileCacheManager
		nMS = (tCurrentFrameTime - m_tUpdateFileCacheManager).GetMilliSecondsAsInt64();

		if (abs(nMS) > UPDATE_FILE_CACHE_MANAGER_IN_MS || tCurrentFrameTime == m_tLastFrameTime) // If tCurrentFrameTime == m_tLastFrameTime the application is paused.
		{
			if (m_pFileCacheManager)
			{
				m_pFileCacheManager->Update();
				m_tUpdateFileCacheManager = tCurrentFrameTime;
			}
		}

		// Process Precache Queue
		ProcessPrecache();

		// Draw Audio Information to the screen
		RenderAudioInformation();
	}

	if (g_nProfiling)
	{
		uint32 nIDsinUse = 0;
		SoundVecIter ItEnd = m_AllSoundsArray.end();
		for (SoundVecIter It=m_AllSoundsArray.begin(); It!=ItEnd; ++It)
		{			
			ISound *pSound = (*It);

			if (pSound)
				++nIDsinUse;
		}

		m_nMaxUsedIDs = max(m_nMaxUsedIDs, nIDsinUse);
	}

	if (UpdateMode & eSoundUpdateMode_Rest)
	{	
		if (g_nSpeakerConfig >= 0 && m_pAudioDevice && g_nSpeakerConfig != m_pAudioDevice->GetSpeakerConfig())
		{
			// The speaker config changed during run time
			// therefore shut down the audio device, set new speaker mode and re-initialize the audio device
			if (Silence(true, true))
			{
				if (DeactivateAudioDevice())
				{
					// Successful shut down, now reset the speaker mode if it's a valid one
					int nOldSpeakerMode = 0;
					ptParamINT32 nSpeakerMode(0);
					m_pAudioDevice->GetParam(adpSPEAKER_MODE, &nSpeakerMode);
					nSpeakerMode.GetValue(nOldSpeakerMode);

					nSpeakerMode = g_nSpeakerConfig;
					if (!m_pAudioDevice->SetParam(adpSPEAKER_MODE, &nSpeakerMode))
					{
						// Invalid speaker mode, reset to the old working one
						nSpeakerMode = nOldSpeakerMode;
						m_pAudioDevice->SetParam(adpSPEAKER_MODE, &nSpeakerMode);
						Log(eSLT_Error, "<Sound> Invalid speaker mode (%d), setting back to (%d)!", g_nSpeakerConfig, nOldSpeakerMode);
						g_nSpeakerConfig = nOldSpeakerMode;
					}

					// Now re-initialize the audio device
					if (!ActivateAudioDevice())
						Log(eSLT_Error, "<Sound> Unable to re-initialize the audio device!");
				}
			}
		}
	}

	if (UpdateMode & eSoundUpdateMode_Sounds)
	{
		for (SoundVecIter it = m_ToBeDeletedSoundObjects.begin(), itEnd = m_ToBeDeletedSoundObjects.end(); it != itEnd; ++ it)
			delete (CSound* const)(*it); // Important to call this only on a CSound instance!!

		m_ToBeDeletedSoundObjects.clear();
	}

	if ((UpdateMode & eSoundUpdateMode_FromMenu) != 0)
	{
		m_pSoundAssetManager->Update(bIgnoreTimers);
	}

	// This is necessary to reliably determine when the application paused.
	m_tLastFrameTime = tCurrentFrameTime;
}

//////////////////////////////////////////////////////////////////////////
bool CSoundSystem::IsSoundPH(ISound *pSound)
{
	//GUARD_HEAP;
	// check if indoor only or outdoor only 
	//if (pSound->GetFlags() & FLAG_SOUND_OUTDOOR)
	//{			
	//	return (!m_bInside); // fade out or fade in
	//}
	
	//if (pSound->GetFlags() & FLAG_SOUND_INDOOR)		
	//{	
	//	return(m_bInside); // fade out or fade in
	//}

	bool bResult = false;
	CSound* const pCSound = (CSound* const)(pSound);

		// at this point check if should use sound occlusion at all
		if (!(pSound->GetFlags() & FLAG_SOUND_CULLING))
			return (true); // always hearable, no occlusion - proceed with fade in
	
		I3DEngine* p3dEngine = gEnv->p3DEngine;
		IVisArea *pSoundArea = NULL;

		if (p3dEngine)	
		{
			pSoundArea = p3dEngine->GetVisAreaFromPos(pCSound->GetPlaybackPosition());	
			pCSound->SetSoundVisArea(pSoundArea);
		}

	// test for all listeners
	CListener* pListener = NULL;
	tVecListenersIter IterEnd = m_vecListeners.end();
	for (tVecListenersIter Iter = m_vecListeners.begin(); Iter != IterEnd; ++Iter)
	{
		pListener = (*Iter);

		if (pListener->bActive)
		{
			// if listener and sound are in the same VisArea (or both in none) then its hearable
			if (pListener->GetVisArea() == pSoundArea)
				return (true);

			// Listener outside, Sound inside
			if (!pListener->GetVisArea() && pSoundArea)
			{
				if (pSoundArea->IsConnectedToOutdoor())
					return (true);

				// test more VisAreas around the SoundArea
				m_SoundTempVisAreas.Clear();
				int nMaxDepth = 3;
				pSoundArea->FindSurroundingVisArea( nMaxDepth, true, &m_SoundTempVisAreas, MAX_VIS_AREAS);

				for(int i=0; i<m_SoundTempVisAreas.Count(); i++)
					if (IVisArea * pArea = m_SoundTempVisAreas.GetAt(i))
						if (pArea->IsConnectedToOutdoor())
							return (true);
			}

			//	// if the listener is not inside a vis area but the sound is,
			//	// then the sound is not hearable 
			//	if (pSound->GetSoundVisArea())
			//		return (false); // fade out

			//	return (true); // both listener and sound in outdoor - fade in

			// Listener inside, Sound outside
			if (pListener->GetVisArea() && !pSoundArea)
			{
				if (pListener->GetVisArea()->IsConnectedToOutdoor())
					return true;

				// test all VisAreas in the listeners cache
				for(int i=0; i<pListener->pVisAreas->Count(); i++)
					if (IVisArea * pArea = pListener->pVisAreas->GetAt(i))
						if (pArea->IsConnectedToOutdoor())
							return (true);
			}

			// both inside, but not in cache of VisAreas
			if (pListener->pVisAreas->Find(pCSound->GetSoundVisArea()) > 0)
				return true;

		}

		// now check that the area where the sound is in, isn't occluded by doors, 
		// objects or buildings	
		//int numToCheck = m_pCVarVisAreaProp->GetIVal();
		//for (int k=0; k < numToCheck; k++)
		//{
		//	IVisArea *pArea=m_VisAreas[k];
		//	if (!pArea)
		//		break;
		//	if (pArea==pSound->GetSoundVisArea())
		//		return (true); // both listener and sound in indoor, and in a not-occluded area - fade in
		//} //k

		//return (false); //fade out

	}
			
	return bResult;
}

//////////////////////////////////////////////////////////////////////////
IMusicSystem* CSoundSystem::CreateMusicSystem()
{
	IMusicSystem* pMusicSystem = new CMusicSystem();

	return pMusicSystem;
}

//////////////////////////////////////////////////////////////////////////
void CSoundSystem::AddToSoundSystem(ISound *pSound)
{
	//GUARD_HEAP;
	
	m_AllSoundsArray[IdToHandle(pSound->GetId()).GetIndex()] = pSound;

	// All Sounds go to the Inactive set
	m_InactiveSounds.AddUnique(pSound);

	CSound* const pCSound = (CSound* const)(pSound);
	pCSound->SetState(eSoundState_Inactive);

}

// remove this later if we dont need it anymore

////////////////////////////////////////////////////////////////////////////
//void CSoundSystem::RegisterOneShotSound( CSound *pSound )
//{
//	m_OneShotSounds.insert( pSound );
//}
//
////////////////////////////////////////////////////////////////////////////
//void CSoundSystem::UnregisterOneShotSound( CSound *pSound )
//{
//	m_stoppedSoundToBeDeleted.insert( pSound );
//	m_OneShotSounds.erase( pSound );
//}

//////////////////////////////////////////////////////////////////////////
void CSoundSystem::SetSoundActiveState(ISound *pSound, ESoundActiveState State)	// take care from where you call this function, because it may cause crashes if you change the state while the system is iterating through the lists (Update())
{
	//GUARD_HEAP;

	CRY_ASSERT(pSound);
	if(!pSound)
	{
		Log(eSLT_Error, "<Sound> SetSoundActiveState called with a null sound");
		return;
	}

	_smart_ptr<CSound> const pCSound = (CSound* const)(pSound);

	if (pCSound->GetState() == State)
		return;

	switch (pCSound->GetState())
	{
		case eSoundState_Active:
		{
			if (pCSound->GetLoopMode())
			{
				m_LoopedSounds.EraseUnique(pSound);
			}
			else
			{
				m_OneShotSounds.EraseUnique(pSound);
			}
			pCSound->Deactivate();
			break;
		}
		case eSoundState_Inactive:
		{
			m_InactiveSounds.EraseUnique(pSound);
			break;
		}		
	}

	pCSound->SetState(State);

	switch (pCSound->GetState())
	{
		case eSoundState_Active:
		{
			if (pCSound->GetLoopMode())
				m_LoopedSounds.AddUnique(pSound);
			else
				m_OneShotSounds.AddUnique(pSound);

			break;
		}
		case eSoundState_Inactive:
		{
			if (pCSound->GetLoopMode())
			{
				m_InactiveSounds.AddUnique(pSound);
			}
			else
			{
				m_stoppedSoundToBeDeleted.AddUnique(pSound);
			}

			break;
		}
		case eSoundState_None:
			{
				break;
			}
	}
}

void CSoundSystem::SetMasterPitch(float fPitch)
{
	if (m_pAudioDevice)
	{
		ptParamF32 fParam(fPitch);
		m_pAudioDevice->SetParam(adpPITCH, &fParam);
	}
}

//! Converts dialog file name syntax into dialog key
const char* CSoundSystem::MakeDialogKey(const char* sDialogFileName)
{
	ParseNameIntoCache(sDialogFileName);
	return m_oParseNameCache.sKeyName.c_str();
}

// takes in string = "group name : sound name" we make 2 strings from one
void CSoundSystem::ParseNameIntoCache(char const* const pcName)
{
	FUNCTION_PROFILER( gEnv->pSystem, PROFILE_SOUND );

	m_oParseNameCache.sFileName			= pcName;
	m_oParseNameCache.sProjectPath	= "";
	m_oParseNameCache.sProjectName	= "";
	m_oParseNameCache.sGroupName		= "";
	m_oParseNameCache.sEventName		= "";
	m_oParseNameCache.sKeyName			= "";
	m_oParseNameCache.nReturnFlags	= 0;

	if (!pcName || !pcName[0])
		return;

	// Format:
	// (   sProjectPath                   ) 
	// xxx/xxx/xxx/xxx / sProjectName : sGroupName : sEventName

	char const* const pPoint  = strstr(pcName, ".");
	char const* const pColon1 = strstr(pcName, ":");

	if (!pPoint && !pColon1)
	{
		// This is a sound key
		m_oParseNameCache.sKeyName      = pcName;
		m_oParseNameCache.sProjectName	= "dialog";
		m_oParseNameCache.sProjectPath  = stack_string(PathUtil::GetLocalizationFolder() + "dialog/");
		m_oParseNameCache.sFileName			= m_oParseNameCache.sProjectPath;
		m_oParseNameCache.sFileName			+= m_oParseNameCache.sKeyName;
		m_oParseNameCache.sFileName			+= ".wav";
		m_oParseNameCache.nReturnFlags	=  PSNRC_KEY|PSNRC_CONVERTEDTOWAV|PSNRC_VOICE;
		return;
	}

	if (pPoint && !pColon1)
	{
		// This is already a wave file, so lets create the keyname
		int nPosLastSlash = m_oParseNameCache.sFileName.rfind('/', m_oParseNameCache.sFileName.size());
		int nPos2ndLastSlash = m_oParseNameCache.sFileName.rfind('/', nPosLastSlash-1);
		
		m_oParseNameCache.sKeyName.assign(m_oParseNameCache.sFileName.substr(nPos2ndLastSlash+1, m_oParseNameCache.sFileName.size()-nPos2ndLastSlash-5));
		m_oParseNameCache.nReturnFlags = PSNRC_WAV;
		return;
	}

	// assign sProjectPath
	m_oParseNameCache.sProjectPath.assign(pcName, pColon1-pcName);
	m_oParseNameCache.sProjectPath += "/";
	m_oParseNameCache.sProjectPath.MakeLower();

	const char *pEnd = pColon1;		

	// find end
	while(*pEnd)
		++pEnd;		

	for(const char *pLastSlash = pColon1; pLastSlash>=pcName; --pLastSlash)
	{
		if(*pLastSlash=='\\' || *pLastSlash=='/')
		{
			// assign sProjectName
			m_oParseNameCache.sProjectName.assign(pLastSlash+1, pColon1-pLastSlash-1);
			m_oParseNameCache.sProjectName.MakeLower();
			break;
		}
		if (pLastSlash == pcName)
		{
			// no slash found, so project name and path are same
   		m_oParseNameCache.sProjectName.assign(pcName, pColon1-pcName);
			m_oParseNameCache.sProjectName.MakeLower();
		}
	}		

	
	char const* const pColon2 = strrchr(pColon1+1,':');

	if(!pColon2)  // no 2nd : -> no eventname, everything behind the : is the groupname
	{
		// assign sGroupName
		m_oParseNameCache.sGroupName.assign(pColon1+1, pEnd-pColon1-1);
		m_oParseNameCache.sGroupName.MakeLower();
		m_oParseNameCache.nReturnFlags = PSNRC_GROUP;
		return;
	}
	else
	{
		// assign sGroupName
		m_oParseNameCache.sGroupName.assign(pColon1+1, pColon2-pColon1-1);
		m_oParseNameCache.sGroupName.MakeLower();
	}

	// invalid event name?
	if (pEnd-pColon2-1 == 0)
	{
		m_oParseNameCache.nReturnFlags = 0;
		return;
	}

	// assign sEventName
	assert (pEnd-pColon2-1 > 0);
	m_oParseNameCache.sEventName.assign(pColon2+1, pEnd-pColon2-1);
	m_oParseNameCache.sEventName.MakeLower();
	assert (!m_oParseNameCache.sEventName.empty());

	if (g_nLanguagesConversion)
	{
		if (m_oParseNameCache.sProjectName == "languages")
		{
			// convert legacy languages events to direct wav-files
			Log(eSLT_Warning, "<Sound> Trying to play an old languages sound event! Please fix this using the WAV file and a Dialog entity for play back. Sound: (%s)",m_oParseNameCache.sFileName.c_str());
			
			// TODO: double check whether this is still needed otherwise make sure it's prefixed with the new localization folder set in sys_localization_folder
			m_oParseNameCache.sFileName = m_oParseNameCache.sProjectName;
			m_oParseNameCache.sFileName += "/dialog/";
			m_oParseNameCache.sFileName += m_oParseNameCache.sGroupName;
			m_oParseNameCache.sFileName += "/";
			m_oParseNameCache.sFileName += m_oParseNameCache.sEventName;
			m_oParseNameCache.sFileName += ".wav";
			
			m_oParseNameCache.sEventName = "";
			Log(eSLT_Warning, "<Sound> Use this file location instead: %s",m_oParseNameCache.sFileName.c_str());
			m_oParseNameCache.nReturnFlags = PSNRC_CONVERTEDTOWAV|PSNRC_VOICE;
			return;
		}
	}

	m_oParseNameCache.nReturnFlags = PSNRC_EVENT;
}

//////////////////////////////////////////////////////////////////////
ISound* CSoundSystem::LoadSound(char const* const sName, uint32 nFlags, uint32 nFlagsExtended, uint32 nPrecacheFlags)
 {
	//GUARD_HEAP;
	FUNCTION_PROFILER( gEnv->pSystem, PROFILE_SOUND );
	MEMSTAT_CONTEXT(EMemStatContextTypes::MSC_Other, 0, "SoundSystem LoadSound");

	if (!m_bSoundSystemEnabled)
		return NULL;

	//if (flags & FLAG_SOUND_3D)	// make all 3d sounds use the sw-attenuation
		//flags|=FLAG_SOUND_RADIUS;
	
	// First reset the m_oParseNameCache by passing just NULL
	ParseNameIntoCache(NULL);

	bool bAddNewEntry = false;
	CCryNameCRC sCacheNameCRC;

	if (g_nCullingByCache && (nFlags & FLAG_SOUND_ADD_TO_CACHE))
	{
		sCacheNameCRC = sName;
		tMapSoundNameCacheCRC::const_iterator ItFindCRC = m_SoundNameCacheCRC.find(sCacheNameCRC);

		if (ItFindCRC == m_SoundNameCacheCRC.end())
		{
			// add new Entry
			bAddNewEntry = true;
		}
	}

	// First parse this name and split it's contents into the name cache
	CSoundBuffer* pBuf = NULL;
	if (sName && sName[0])
	{
		CCryNameCRC oBufferCRC;
		char cGroupName[MAX_GROUPPATHLENGTH]	= {'\0'};
		char cEventName[MAX_EVENTNAMELENGTH]	= {'\0'};

		switch (GetGroupAndEventNameFast(sName, cGroupName, cEventName))
		{
		case PSNRC_GROUP:
		case PSNRC_EVENT:
			{
				if (cEventName[0])
				{
					oBufferCRC = cGroupName;
					pBuf = m_pSoundAssetManager->GetSoundBuffer(oBufferCRC);

					if (pBuf)
					{
						if (pBuf->GetProps()->nProjectIndex != PROJECTINDEX_INVALID)
						{
							// The buffer has been found, update the event name member since it won't be parsed in this case
							m_oParseNameCache.sEventName = cEventName; // this is necessary to create a the correct platform sound
						}
						else
						{
							// The project file needs to be reloaded
							ParseNameIntoCache(sName);
							PrecacheSound(pBuf, nFlags, nPrecacheFlags);
						}
					}
				}
				else
					return NULL; // Here we have just a group path
			}
			break;
		case PSNRC_KEY:
			{
				stack_string sFullVoicePath;
				GetFullVoicePathFast(sName, sFullVoicePath); // Optimize for one time mission hints

				if (!sFullVoicePath.empty())
				{
					oBufferCRC	= sFullVoicePath.c_str();
					pBuf				= m_pSoundAssetManager->GetSoundBuffer(oBufferCRC);

					if (pBuf)
					{
						m_oParseNameCache.sKeyName			= sName;
						m_oParseNameCache.sProjectName	= "dialog";
						m_oParseNameCache.sProjectPath  = stack_string(PathUtil::GetLocalizationFolder() + "dialog/");
						m_oParseNameCache.sFileName			= sFullVoicePath;
						m_oParseNameCache.nReturnFlags	=  PSNRC_KEY|PSNRC_CONVERTEDTOWAV|PSNRC_VOICE;

						// Update the precache flags.
						pBuf->GetProps()->nPrecacheFlags = nPrecacheFlags;
					}
				}
			}
			break;
		case PSNRC_WAV:
			{
				// It's a wave file, replace the .wav extension with the one the system is using preferably atm
				stack_string sFullVoicePath = sName;
				sFullVoicePath.replace(sFullVoicePath.size()-4, 4, GetCurrentCompressionFast());
				oBufferCRC	= sFullVoicePath.c_str();
				pBuf				= m_pSoundAssetManager->GetSoundBuffer(oBufferCRC);
				
				if (pBuf)
				{
					m_oParseNameCache.sFileName			= sFullVoicePath;
					m_oParseNameCache.nReturnFlags	= PSNRC_WAV;

					size_t const nPosLastSlash			= m_oParseNameCache.sFileName.rfind('/', m_oParseNameCache.sFileName.size());
					size_t const nPos2ndLastSlash		= m_oParseNameCache.sFileName.rfind('/', nPosLastSlash-1);
					m_oParseNameCache.sKeyName.assign(m_oParseNameCache.sFileName.substr(nPos2ndLastSlash+1, m_oParseNameCache.sFileName.size()-nPos2ndLastSlash-5));
				}
			}
			break;
		}

		// If the buffer hasn't been found in the CRC map or it's a voice line
		if (!pBuf)
		{
			ParseNameIntoCache(sName);

			if (m_oParseNameCache.nReturnFlags)
			{
				bool bOldValue			= m_bDelayPrecaching;
				m_bDelayPrecaching	= false;
				pBuf								= PrecacheSound(NULL, nFlags, nPrecacheFlags);
				m_bDelayPrecaching	= bOldValue;
			}
		}
	}

	if (!pBuf)
	{
		//m_DebugLogger.LogString(sTemp + szFile);

		// if project ready, then the sound most likely is unknown
		if (!(m_ErrorCode & eSoundSystemErrorCode_ProjectNotReady))
		{
			m_ErrorCode = (ESoundSystemErrorCode) (m_ErrorCode | eSoundSystemErrorCode_SoundNotKnown);
			Log(eSLT_Error, "<Sound> Create sound \"%s\" failed - Invalid Buffer! Does file exist?", sName);
		}

		return NULL;
	}

	//GUARD_HEAP;
	//create a new instance	
	_smart_ptr<ISound> pISound = GetNewSoundObject();
	CSound* const pSound = (CSound* const)(pISound.get());

	//GUARD_HEAP;
	 
	if (!pSound)
	{
		Log(eSLT_Warning, "<Sound> Create sound %s failed! No free sound availible.", sName);
		m_ErrorCode = (ESoundSystemErrorCode) (m_ErrorCode | eSoundSystemErrorCode_SoundCreateFailed);
		return NULL; // something wrong
	}

	if (pSound->GetId() == INVALID_SOUNDID)
	{
		Log(eSLT_Warning, "<Sound> Create sound %s failed! No SoundID left.", sName);
		m_ErrorCode = (ESoundSystemErrorCode) (m_ErrorCode | eSoundSystemErrorCode_SoundIDRangeFull);
		return NULL; // bad soundid, salt array full?!
	}

	pSound->SetSoundBufferPtr(pBuf);

	if (!pBuf->Loaded(false) && (nPrecacheFlags & FLAG_SOUND_PRECACHE_LOAD_SOUND))
		pBuf->Load(pSound);

	//if (m_oParseNameCache.nReturnFlags == PSNRC_GROUP)
	//{
	//	// The provided sound name was only a group, spawning an event is not possible
	//	Log(eSLT_Warning, "<Sound> Create sound %s failed! Only a group name was provided.", szFile);
	//	return NULL; // something wrong
	//}

	// Catch warning
	//if (pBuf->GetProps()->eBufferType == btSAMPLE && !(nFlags & FLAG_SOUND_VOICE))
	//{
	//	Log(eSLT_Warning, "<Sound> Sample sound %s not marked as Voice!", szFile);
	//}

	// remove the voice flags on event sounds
	if (pBuf->GetProps()->eBufferType == btEVENT && (nFlags & FLAG_SOUND_VOICE))
	{
		// If the flag is set, we WANT the lip synch, regardless of the buffer type
		//nFlags &= ~FLAG_SOUND_VOICE;
		//int nEventHasVoiceFlag = 0;
		//assert(nEventHasVoiceFlag);
	}

	TFixedResourceName sLocalEventName = m_oParseNameCache.sEventName;

	if (pBuf->GetProps()->eBufferType == btSAMPLE && (nFlags & FLAG_SOUND_VOICE) && (m_oParseNameCache.nReturnFlags & (PSNRC_WAV|PSNRC_KEY|PSNRC_VOICE)) )
	{
		SLocalizedSoundInfoGame* pSoundInfo = new SLocalizedSoundInfoGame; // memory will be assigned to sound object
		
		if (!pSoundInfo)
			return false;

		bool bSuccess			= false;
		bool bSearchAgain	= false;

		// first guess on allocs
		memset(pSoundInfo, 0, sizeof(SLocalizedSoundInfoGame));
		pSoundInfo->nNumSoundMoods = m_nDialogSoundMoodAlloc;
		pSoundInfo->nNumEventParameters = m_nDialogEventParameterAlloc;
		SLocalizedAdvancesSoundEntry* pSoundMoodAlloc = NULL;
		SLocalizedAdvancesSoundEntry* pEventParameterAlloc = NULL;

		if (m_nDialogSoundMoodAlloc > 0)
		{
			pSoundMoodAlloc = new SLocalizedAdvancesSoundEntry[m_nDialogSoundMoodAlloc];
			pSoundInfo->pSoundMoods = pSoundMoodAlloc;
		}

		if (m_nDialogEventParameterAlloc > 0)
		{
			pEventParameterAlloc = new SLocalizedAdvancesSoundEntry[m_nDialogEventParameterAlloc];
			pSoundInfo->pEventParameters = pEventParameterAlloc;
		}
		
		bSuccess = gEnv->pSystem->GetLocalizationManager()->GetLocalizedInfoByKey( m_oParseNameCache.sKeyName.c_str(), pSoundInfo);
 		
		if (!bSuccess)
		{
			// more SoundMoods than expected, lets increase the buffer
			if (pSoundInfo->nNumSoundMoods > m_nDialogSoundMoodAlloc)
			{
				if (pSoundMoodAlloc)
					delete[] pSoundMoodAlloc;

				m_nDialogSoundMoodAlloc = pSoundInfo->nNumSoundMoods;
				pSoundMoodAlloc = new SLocalizedAdvancesSoundEntry[m_nDialogSoundMoodAlloc];
				pSoundInfo->pSoundMoods = pSoundMoodAlloc;
				bSearchAgain = true;
			}

			// more EventParameters than expected, lets increase the buffer
			if (pSoundInfo->nNumEventParameters > m_nDialogEventParameterAlloc)
			{
				if (pEventParameterAlloc)
					delete[] pEventParameterAlloc;

				m_nDialogEventParameterAlloc = pSoundInfo->nNumEventParameters;
				pEventParameterAlloc = new SLocalizedAdvancesSoundEntry[m_nDialogEventParameterAlloc];
				pSoundInfo->pEventParameters = pEventParameterAlloc;
				bSearchAgain = true;
			}

			if (bSearchAgain)
				bSuccess = gEnv->pSystem->GetLocalizationManager()->GetLocalizedInfoByKey( m_oParseNameCache.sKeyName.c_str(), pSoundInfo);
			
		}

		if (bSuccess)
		{
			pSound->SetVoiceSpecs(pSoundInfo);
			sLocalEventName = pSoundInfo->sSoundEvent; 

			if ( sLocalEventName.empty() ) // no eventname found, but this is still a voice line, so lets choose clean
				sLocalEventName = "clean";
		}
		else
		{
			// If the flag is set, we WANT the lip synch, regardless of the localization manager
			//nFlags &= ~FLAG_SOUND_VOICE; // voice files have to be in the localization manager
			//Log(eSLT_Warning, "<Sound> Sound key %s not found in Localization Manager!", m_oParseNameCache.sKeyName.c_str());

			SAFE_DELETE_ARRAY(pSoundInfo->pSoundMoods);
			SAFE_DELETE_ARRAY(pSoundInfo->pEventParameters);
			delete pSoundInfo;
		}
	}
	
	if (!sLocalEventName.empty() || (m_oParseNameCache.nReturnFlags & PSNRC_GROUP))
		nFlags |= FLAG_SOUND_EVENT;
	else
		nFlags &= ~FLAG_SOUND_EVENT;

	CSoundBuffer *pInfoBuf = NULL;

	if (pBuf->GetProps()->eBufferType == btSAMPLE && (nFlags & FLAG_SOUND_EVENT))
	{
		pInfoBuf = m_pSoundAssetManager->GetSoundBuffer(m_oDialogBufferCRC);

		if (!pInfoBuf)
		{
			if ( sLocalEventName.empty() )
				sLocalEventName = "clean";

			// TODO: replace hard coded dialog project with cvar
			m_oParseNameCache.sFileName = "sounds/dialog:dialog:" + sLocalEventName;

			ParseNameIntoCache(m_oParseNameCache.sFileName);
			pInfoBuf = PrecacheSound(NULL, nFlags, FLAG_SOUND_PRECACHE_LOAD_SOUND);
		}
		
		if (!pInfoBuf)
		{
			Log(eSLT_Error, "<Sound> Cannot precache sound: %s", sName);
			m_ErrorCode = (ESoundSystemErrorCode) (m_ErrorCode | eSoundSystemErrorCode_SoundNotKnown);
			return NULL;
		}

		pSound->m_pEventGroupBuffer = pInfoBuf;
		pSound->m_pEventGroupBuffer->AddToLoadReqList(pSound);
	}
	else
	{
		pInfoBuf = pBuf;
	}

	// Now create the platform sound
	IPlatformSound* pPlatformSound	= m_pAudioDevice->CreatePlatformSound(pSound, sLocalEventName.c_str());

	if (!pPlatformSound)
	{
		delete pPlatformSound;
		m_ErrorCode = (ESoundSystemErrorCode) (m_ErrorCode | eSoundSystemErrorCode_SoundNotKnown);
		return NULL; // something wrong
	}

	pSound->SetPlatformSoundPtr(pPlatformSound);

	// support allow unload after the sound got played
	if (pBuf->GetProps()->nPrecacheFlags & FLAG_SOUND_PRECACHE_UNLOAD_AFTER_PLAY)
	{
		pSound->AddEventListener(pBuf, "SoundBuffer");
	}

	GetError(); // reset previous error
	ESoundSystemErrorCode eResult = pSound->Initialize(pInfoBuf, nFlags, nFlagsExtended);

	if (eResult != eSoundSystemErrorCode_None)
	{
		m_ErrorCode = (ESoundSystemErrorCode) (m_ErrorCode | eResult);

		if (eResult == eSoundSystemErrorCode_SoundNotKnown)
		{
			return NULL;
		}
	}

	//Log(eSLT_Message, "<Sound> %s loaded, sound size=%d",szFile,m_setSounds.size());

	// Add to SoundNameCache
	if (bAddNewEntry && pSound->IsInitialized())
	{
		SSoundCacheInfoInternal NewEntry;
		NewEntry.bLooping = (pSound->GetFlags() & FLAG_SOUND_LOOP) ? true : false;
		NewEntry.b2D = (pSound->GetFlags() & FLAG_SOUND_2D) ? true : false;
		NewEntry.fMaxRadiusSq = pSound->GetSquardedMaxRadius();

		m_SoundNameCacheCRC[sCacheNameCRC] = NewEntry;
	}

	AddToSoundSystem(pSound);

	if (g_nDebugSound == SOUNDSYSTEM_DEBUG_SIMPLE)
	{
		// if there is a playback filter set, then we dont want to spam the console
		// sadly on creating the sound the eSemantic on the sound is not yet availible
		// so we block all logging
		if (!g_nPlaybackFilter)
			Log(eSLT_Message, "<Sound> Create Sound %s, ID:%d", pSound->GetName(), pSound->GetId());
	}

	if (gEnv->pCryPak->GetLvlResStatus() || g_nProfiling)
	{
		// dont add event groups into sound info map
		if (!(m_oParseNameCache.nReturnFlags & PSNRC_GROUP))
		{
			ISoundProfileInfo* pSoundInfo = NULL;

			tmapSoundInfo::iterator It = m_SoundInfos.find(CONST_TEMP_STRING(m_oParseNameCache.sFileName));

			if (It == m_SoundInfos.end())
			{
				pSoundInfo = new CSoundProfileInfo(m_oParseNameCache.sFileName);

				if (pSoundInfo)
					m_SoundInfos[m_oParseNameCache.sFileName] = pSoundInfo;
			}
			else
				pSoundInfo = (*It).second;

			pSoundInfo->GetInfo()->nSoundIDsUsed = m_nMaxUsedIDs;

			if (!pSoundInfo->GetInfo()->bWavebanksLogged)
			{
				// create all wavebanks dependencies for this Soundinfo
				pSound->GetSoundBufferPtr()->LogDependencies();
				pSoundInfo->GetInfo()->bWavebanksLogged = true;
			}
		}
	}

	return (pSound);
}

//////////////////////////////////////////////////////////////////////
void CSoundSystem::RemoveSound(tSoundID nSoundID)
{
	ISound *pSound = GetSound(nSoundID);
	RemoveReference(pSound);
}


//////////////////////////////////////////////////////////////////////////
void	CSoundSystem::RecomputeSoundOcclusion(bool bRecomputeListener, bool bForceRecompute, bool bReset)
{
	FUNCTION_PROFILER( gEnv->pSystem, PROFILE_SOUND );

	if (bReset)
	{
		tVecListenersIter IterEnd = m_vecListeners.end();
		for (tVecListenersIter Iter = m_vecListeners.begin(); Iter != IterEnd; ++Iter)
		{
			CListener* pListener = (*Iter);
			pListener->SetVisArea(NULL);
		}

		SoundVecIter ItEnd = m_AllSoundsArray.end();
		for (SoundVecIter It=m_AllSoundsArray.begin(); It!=ItEnd; ++It)
		{
			CSound* const pSound = (CSound* const)(*It);

			if (pSound)
			{
				pSound->InvalidateVisArea();
			}
		}

		return;
	}

	if (m_nMuteRefCnt > 0)
		return; // don't do this during mute (usually during loading)

	tVecListenersIter IterEnd = m_vecListeners.end();
	for (tVecListenersIter Iter = m_vecListeners.begin(); Iter != IterEnd; ++Iter)
	{
		CListener* pListener = (*Iter);

		I3DEngine	*p3dEngine = gEnv->p3DEngine;
		if (p3dEngine)	
		{			
			//check where the listener is 	
			IVisArea *pCurrArea = NULL;

			if (bRecomputeListener)
			{
				//IListener* pListener = GetListener(LISTENERID_STANDARD);
				//if (!pListener)
				//	return;

				Vec3 vPos = pListener->GetPosition();
				pCurrArea = p3dEngine->GetVisAreaFromPos(vPos);
			}
			else
				pCurrArea = pListener->GetVisArea();

			// avoid silly people to mess around with too many visareas
			int nMaxVis = (g_nVisAreaPropagation & (MAX_VIS_AREAS-1));

			if ((pCurrArea != pListener->GetVisArea()) || (bForceRecompute))
			{
				// listener has changed sector or has moved from indoor to outdoor or viceversa
				pListener->SetVisArea(pCurrArea);
				pListener->pVisAreas->Clear();

				//memset(m_VisAreas,0,sizeof(m_VisAreas));

				if (pListener->GetVisArea())
				{
					//PodArray<IVisArea*> pAreas;
					pListener->GetVisArea()->FindSurroundingVisArea( nMaxVis, true, pListener->pVisAreas, MAX_VIS_AREAS);
					//m_VisAreas.Add(m_pVisArea);


					// if inside let's get a list of visible areas to use for sound occlusion
					//int nAreas=m_pVisArea->GetVisAreaConnections(m_VisAreas,nMaxVis,true);

					// engine doesn't return the current area - be sure to add it or
					// all the sounds currently playing in the listener area will fade out
					//if (nAreas<nMaxVis)
					//m_VisAreas[nAreas]=m_pVisArea;
					//else
					// override the first one - this one is more important
					//m_VisAreas[0]=m_pVisArea;
				}

			}			
		}
	}
}

//////////////////////////////////////////////////////////////////////
void CSoundSystem::RemoveReference(ISound *pSound)
{
	//GUARD_HEAP;

	if (pSound) 
	{
		tSoundID nSoundID = pSound->GetId();
		if(m_SoundSaltBuffer.IsUsed(IdToHandle(nSoundID).GetIndex()))		
		{
			m_AllSoundsArray[IdToHandle(nSoundID).GetIndex()] = NULL;
			DestroySoundID(nSoundID);
		}
	}
}

//////////////////////////////////////////////////////////////////////
ISound* CSoundSystem::GetSound(tSoundID nSoundID) const
{
	if(m_SoundSaltBuffer.IsValid(IdToHandle(nSoundID)))
		return m_AllSoundsArray[IdToHandle(nSoundID).GetIndex()];
	else
		return NULL;
}

//////////////////////////////////////////////////////////////////////
//void CSoundSystem::PlaySound(tSoundID nSoundID)
//{
//	//GUARD_HEAP;
//	CSound *pSound = (CSound *)GetSound(nSoundID);
//	if (pSound)	
//		pSound->Play();	
//}

//////////////////////////////////////////////////////////////////////
bool CSoundSystem::SetListener(const ListenerID nListenerID, const Matrix34 &matTransformation, const Vec3 &vVel, bool bActive, float fRecordLevel)
{
	if (!matTransformation.IsValid() || !vVel.IsValid() || !NumberValid(fRecordLevel))
	{
		Log(eSLT_Warning, "<Sound> Invalid input values (possible NaNs) on SetListener()");
		assert(0);
		return false;
	}

	bool bResult = false;
	Vec3 vOldPos;
	tVecListenersIter Iter = m_vecListeners.begin();
	tVecListenersIter IterEnd = m_vecListeners.end();
	CListener* pListener = NULL;
  
	for (Iter = m_vecListeners.begin(); Iter != IterEnd; ++Iter)
	{
		pListener = (*Iter);

		if (pListener->GetID() == nListenerID)
		{
			vOldPos = pListener->GetPosition();
			pListener->SetMatrix(matTransformation);
			pListener->bActive = bActive;
			pListener->fRecordLevel = fRecordLevel;
		
			bResult = true;
			break;
		}
	}

	if (!bResult)
		return false;

	if(bActive)
	{
		for (Iter = m_vecListeners.begin(); Iter != IterEnd; ++Iter)
		{
			if(pListener !=  (*Iter))
				(*Iter)->bActive = false;
		}
	}

	// Continue only with the listener's velocity if its position actually moved a bit.
	if (m_bSoundSystemEnabled)
	{
		Vec3 v3ListenerVelocity(0.0f, 0.0f, 0.0f);

		if (pListener->bMoved)
		{
			Vec3 const vPos         = matTransformation.GetTranslation();
			Vec3 const vPosDelta    = (vPos - vOldPos);
			float const fTimeDelta  = gEnv->pTimer->GetFrameTime();
			Vec3 vNewVelocity       = fTimeDelta>0.f ? vPosDelta * (g_fDopplerScale/fTimeDelta) : Vec3(0);

			vNewVelocity.x = clamp(vNewVelocity.x, -30.0f, 30.0f);
			vNewVelocity.y = clamp(vNewVelocity.y, -30.0f, 30.0f);
			vNewVelocity.z = clamp(vNewVelocity.z, -30.0f, 30.0f);

			v3ListenerVelocity = pListener->GetVelocity();
			v3ListenerVelocity += vNewVelocity;
			v3ListenerVelocity *= 0.5f;

			// Prevent dividing down to a DEN float as this currently causes trouble in "set3DListenerAttributes".
			v3ListenerVelocity.x = (fabsf(v3ListenerVelocity.x) > FLT_EPSILON) ? v3ListenerVelocity.x : 0.0f;
			v3ListenerVelocity.y = (fabsf(v3ListenerVelocity.y) > FLT_EPSILON) ? v3ListenerVelocity.y : 0.0f;
			v3ListenerVelocity.z = (fabsf(v3ListenerVelocity.z) > FLT_EPSILON) ? v3ListenerVelocity.z : 0.0f;

			pListener->SetVelocity(v3ListenerVelocity); // averaging with last Velocity
		}
		else
		{
			// We're not moving so make sure to set the velocity to 0.
			pListener->SetVelocity(v3ListenerVelocity);
		}
	}

	return bResult;
}

void CSoundSystem::SetListenerEntity( ListenerID nListenerID, EntityId nEntityID )
{
	CListener * pListener = (CListener*)GetListener(nListenerID);

	if (pListener)
		pListener->nEntityID = nEntityID;
}

ListenerID CSoundSystem::CreateListener()
{
	// Create Unused ListenerID
	int32 nNewListenerID = LISTENERID_STANDARD;
	tVecListenersIter IterEnd = m_vecListeners.end();
	for (tVecListenersIter Iter = m_vecListeners.begin(); Iter != IterEnd; ++Iter)
	{
		CListener* pListener = (*Iter);
		
		if (nNewListenerID == pListener->nListenerID)
			++nNewListenerID;
		else
			break;
		//nNewListenerID = max(pListener->nListenerID, nNewListenerID);
	}
	

	// TODO verify ID Management always picks highest free ID. Could search for smallest.
	if (nNewListenerID > 32000)
	{
		Log(eSLT_Always, "Cannot create new Listener. Too many created.");
		return (LISTENERID_INVALID);
	}

	// Setting up the new Listener
	POOL_NEW_CREATE(CListener, pNewListener);

	if (!pNewListener)
		return LISTENERID_INVALID;

	if (m_vecListeners.empty())
	{
		// Setting up the standard Listener
		pNewListener->nListenerID		= LISTENERID_STANDARD;
		pNewListener->bActive				= false; //true
		pNewListener->fRecordLevel	=	1.0;
		m_vecListeners.push_back(pNewListener);
		return LISTENERID_STANDARD;
	}
	else
	{
		pNewListener->nListenerID = nNewListenerID;
		m_vecListeners.push_back(pNewListener);
	}

	return (nNewListenerID);
}

bool CSoundSystem::RemoveListener(ListenerID nListenerID)
{
	bool bResult = false;

	tVecListenersIter IterEnd = m_vecListeners.end();
	for (tVecListenersIter Iter = m_vecListeners.begin(); Iter != IterEnd; ++Iter)
	{
		if ((*Iter)->nListenerID == nListenerID)
		{
			delete *Iter;
			m_vecListeners.erase(Iter);
			bResult = true;
			break;
		}
	}

	if (bResult)
	{
		if (m_vecListeners.empty())
		{
			stl::free_container(m_vecListeners);
		}

		return true;
	}

	return false;
}

ListenerID	CSoundSystem::GetClosestActiveListener(Vec3 const& vPosition) const
{
	bool bFirstTime = true;
	float fDistance = 0.0f;
	tVecListenersIterConst ClosestIter = m_vecListeners.begin();
	tVecListenersIterConst IterEnd = m_vecListeners.end();
	for (tVecListenersIterConst Iter = m_vecListeners.begin(); Iter != IterEnd; ++Iter)
	{
		if ((*Iter)->bActive == true)
		{
			float fTempDistance = (*Iter)->GetPosition().GetSquaredDistance(vPosition);
			if (bFirstTime)
			{
				fDistance = fTempDistance;
			}

			if (fDistance >= fTempDistance)
			{
				bFirstTime = false;
				fDistance = fTempDistance;
				ClosestIter = Iter;
			}
		}
	}

	if (!bFirstTime)
		return (*ClosestIter)->nListenerID;
	else
		return LISTENERID_INVALID;

}

uint32 CSoundSystem::GetNumActiveListeners() const 
{ 
	uint32 nNumActiveListeners = 0;

	tVecListenersIterConst IterEnd = m_vecListeners.end();
	for (tVecListenersIterConst Iter = m_vecListeners.begin(); Iter != IterEnd; ++Iter)
	{
		if ((*Iter)->bActive)
		{
			++nNumActiveListeners;
		}
	} 

	return nNumActiveListeners;
}

IListener* const CSoundSystem::GetListener(ListenerID const nListenerID) const
{
	tVecListenersIterConst const IterEnd = m_vecListeners.end();
	for (tVecListenersIterConst Iter = m_vecListeners.begin(); Iter != IterEnd; ++Iter)
	{
		if ((*Iter)->nListenerID == nListenerID)
		{
			return (IListener*)(*Iter);
		}
	}

	return NULL;
}

IListener* const CSoundSystem::GetListener(EntityId const nEntityID) const
{
	tVecListenersIterConst const IterEnd = m_vecListeners.end();
	for (tVecListenersIterConst Iter = m_vecListeners.begin(); Iter != IterEnd; ++Iter)
	{
		if ((*Iter)->nEntityID == nEntityID)
		{
			return (IListener*)(*Iter);
		}
	}

	return NULL;
}

IListener* CSoundSystem::GetNextListener(ListenerID nListenerID)
{
	CListener* pHighListener = NULL;
	CListener* pNextListener = NULL;

	tVecListenersIter IterEnd = m_vecListeners.end();
	for (tVecListenersIter Iter = m_vecListeners.begin(); Iter != IterEnd; ++Iter)
	{
		if ((*Iter)->nListenerID > nListenerID)
		{
			// if ID is higher than 
			pHighListener = (*Iter);

			
			// if this is the first Listener we found, its the best we have till now
			if (!pNextListener)
				pNextListener = pHighListener;

			if ((*Iter)->nListenerID < pNextListener->nListenerID)
			{
				// if the current ListenerID is smaller than the one we found before, lets switch
				pNextListener = pHighListener;
			}
		}
	}
	// NULL if no higher ID was found
	return (IListener*)pNextListener;
}
//////////////////////////////////////////////////////////////////////////
float CSoundSystem::GetSqDistanceToClosestListener(ISound* pSound)
{
	assert (pSound);

	CSound* const pCSound = (CSound* const)(pSound);

	IListener* pListener = GetListener(GetClosestActiveListener(pCSound->GetPlaybackPosition()));

	if (!pListener)
		return 0.0f;

	return pListener->GetPosition().GetSquaredDistance(pCSound->GetPlaybackPosition());
}

//////////////////////////////////////////////////////////////////////
bool	CSoundSystem::Silence(bool bStopLoopingSounds, bool bUnloadData)
{	
	//GUARD_HEAP;

	// stops all sounds from playing does not need a param
	if (m_pAudioDevice)
	{
		m_pAudioDevice->SetParam(adpSTOPALL_CHANNELS, NULL);
	}

	SoundVecIter ItEnd = m_AllSoundsArray.end();
	for (SoundVecIter It=m_AllSoundsArray.begin(); It!=ItEnd; ++It)
	{		
		_smart_ptr<CSound> const pSound = (CSound* const)(*It);

		if (pSound)
		{
			if (!pSound->GetLoopMode() || bStopLoopingSounds)
			{
				pSound->Stop(ESoundStopMode_AtOnce);
			}

			//*It = NULL;
		}
	}


	// 1st call to Audiodevice to invaliding projects and by this enforcing all buffers to "forget" their handles
	if (m_pAudioDevice && bUnloadData)
		m_pAudioDevice->ResetAudio();

	if (m_pSoundAssetManager && bUnloadData)
		m_pSoundAssetManager->UnloadAllSoundBuffers();

	// 2nd call for now maybe redundant
	if (m_pAudioDevice && bUnloadData)
		m_pAudioDevice->ResetAudio();

	if (m_pMoodManager)
		m_pMoodManager->Reset(bUnloadData);

	if (m_pReverbManager)
		m_pReverbManager->Reset();

	m_SoundNameCacheCRC.clear();

	// cleanup all old Sounds
	ESoundUpdateMode UpdateMode = (ESoundUpdateMode)(eSoundUpdateMode_Sounds | eSoundUpdateMode_CleanInactive);
	Update(UpdateMode);

	// reset Directional Attenuation (Binoculars)
	m_DirAttPos = Vec3(0);
	m_DirAttDir = Vec3(0);
	m_fDirAttCone = 1.0f;
	m_fDirAttMaxScale = 0.0f;
	m_bNeedSoundZoomUpdate = true;

	return true;
}

void CSoundSystem::SetPlaybackFilter(const uint32 nMask)
{
	if (nMask != 0)
	{
		SoundVecIter ItEnd = m_AllSoundsArray.end();
		for (SoundVecIter It=m_AllSoundsArray.begin(); It!=ItEnd; ++It)
		{
			_smart_ptr<CSound> const pSound = (CSound* const)(*It);

			if (pSound && (pSound->GetSemantic() & nMask) == 0)
			{
				pSound->Stop(ESoundStopMode_AtOnce);
			}
		}
	}

	g_nPlaybackFilter = nMask;
}

//////////////////////////////////////////////////////////////////////////
bool CSoundSystem::DeactivateAudioDevice() 
{
	bool bResult   = false;
	g_nSoundEnable = 0; 
	
	Update(eSoundUpdateMode_All);
	
	// Shut the music system down first.
	if (!gEnv->pMusicSystem->IsNullImplementation())
	{
		CMusicSystem* const pMusicSystem = static_cast<CMusicSystem* const>(gEnv->pMusicSystem);

		if (pMusicSystem)
		{
			pMusicSystem->Shutdown();
		}
	}

	// Make sure to shut the audio device down last.
	if (m_pAudioDevice)
	{
		bResult = m_pAudioDevice->ShutDownDevice();
	}
	
	return bResult;
}

//////////////////////////////////////////////////////////////////////////
bool CSoundSystem::ActivateAudioDevice() 
{
	bool bResult   = false;
	g_nSoundEnable = 1;

	if (m_pAudioDevice)
	{
		AudioDeviceSettings InitSettings;
		m_pAudioDevice->GetInitSettings(&InitSettings);

		InitSettings.nHWChannels       = g_nHWChannels;
		InitSettings.nSoftwareChannels = g_nSoftwareChannels;
		InitSettings.nSpeakerConfig    = g_nSpeakerConfig;

		m_pAudioDevice->SetInitSettings(&InitSettings);
		bResult = m_pAudioDevice->InitDevice(this);
	}

	if (!gEnv->pMusicSystem->IsNullImplementation())
	{
		CMusicSystem* const pMusicSystem = static_cast<CMusicSystem* const>(gEnv->pMusicSystem);

		if (pMusicSystem)
		{
			pMusicSystem->Init();
		}
	}

	return bResult;
}


//////////////////////////////////////////////////////////////////////////
void	CSoundSystem::Pause(bool bPause, bool bResetVolume, unsigned int nFadeOutInMS)
{
	m_bSoundSystemPause = bPause;
	m_bIsInPauseCall = true;

	if (nFadeOutInMS == 0)
	{
		// Use local copies as sounds could get removed during iteration.
		SSoundContainer apTempOneShotSounds(m_OneShotSounds);
		SSoundContainer apTempLoopedSounds(m_LoopedSounds);
		SSoundContainer apTempInactiveSounds(m_InactiveSounds);
		SSoundContainer::const_iterator const IterOneShotsEnd(apTempOneShotSounds.end());
		SSoundContainer::const_iterator const IterLoopedEnd(apTempLoopedSounds.end());
		SSoundContainer::const_iterator const IterInactiveEnd(apTempInactiveSounds.end());

		// One shot sounds
		for (SSoundContainer::const_iterator Iter(apTempOneShotSounds.begin()); Iter != IterOneShotsEnd; ++Iter)
		{
			CSound* const pSound = (CSound* const)((*Iter).get());
			// inactive sounds should not be touched at all because if they are paused while inactive,
			// they will be triggered again
			if (pSound && !(pSound->GetFlags() & FLAG_SOUND_EDITOR) && pSound->GetState() == eSoundState_Active)
			{
				pSound->SetPaused(bPause);
			}
		}

		// Looping sounds
		for (SSoundContainer::const_iterator Iter(apTempLoopedSounds.begin()); Iter != IterLoopedEnd; ++Iter)
		{
			CSound* const pSound = (CSound* const)((*Iter).get());

			if (pSound && !(pSound->GetFlags() & FLAG_SOUND_EDITOR))
			{
				pSound->SetPaused(bPause);
			}
		}

		// Inactive sounds
		for (SSoundContainer::const_iterator Iter(apTempInactiveSounds.begin()); Iter != IterInactiveEnd; ++Iter)
		{
			CSound* const pSound = (CSound* const)((*Iter).get());

			if (pSound && !(pSound->GetFlags() & FLAG_SOUND_EDITOR) && pSound->GetState() == eSoundState_Active)
			{
				pSound->SetPaused(bPause);
			}
		}
	}
	else
	{
		m_nFadeOutInMS = nFadeOutInMS;
	}


  //ptParamBOOL bTemp(bPause);
  //m_pAudioDevice->SetParam(adpPAUSED_STATUS, &bTemp);
    
	if (bResetVolume)
	{
		// this is needed for instance when going back to menu
		// after setting the sound volume to 0. The sound volume
		// calculation takes the general volume into account,
		// so if it was set to 0, when going to menu the sliders
		// and buttons won't produce any sound, because the sound system
		// is not updated anymore so it doesn't have a chance to 
		// update the general volume. Hence we reset the volume to 1,
		// and the individual volume of sound and music sliders is set
		// by the menu scripts calling setvolume
		//m_fSFXResetVolume=m_pCVarSFXVolume->GetFVal();
		//m_pCVarSFXVolume->Set(1.0f);				
		m_bResetVolume	= true;
		//m_fSFXVolume		= 1.0f; //m_pCVarSFXVolume->GetFVal();				
		g_SoundCVars.g_fGameSFXVolume		= 1.0f;
		g_SoundCVars.g_fGameMusicVolume	= 1.0f;
	}
	
	if (!bPause && m_bResetVolume)
	{	
		// restore normal volume back
		//m_pCVarSFXVolume->Set(m_fSFXResetVolume);				
		m_bResetVolume = false;

    // TODO REPLACE THE SYSTEM VAR CALL
		m_fSFXVolume = g_SoundCVars.g_fSFXVolume;
		//SetGroupScale(SOUNDSCALE_MISSIONHINT,m_fSFXVolume*0.65f);		
	}

	m_bIsInPauseCall = false;

}

//////////////////////////////////////////////////////////////////////////
void	CSoundSystem::Mute(bool bMute)
{
	//GUARD_HEAP;
	if (bMute)
		m_nMuteRefCnt++;
	else
		m_nMuteRefCnt--;
	if (m_nMuteRefCnt < 0)
		m_nMuteRefCnt = 0;
	bool bSetMute = (m_nMuteRefCnt != 0);
  ptParamINT32 muteParam(bSetMute ? 0 : 255);
  m_pAudioDevice->SetParam(adpMUTE_STATUS, &muteParam);
	// TODO: uses differnt types int32/bool, eitherway use bool to set mute  or int32 to set ADP_MASTER_VOLUME
	// hard coded 255 is always bad! us SFXMastervolume and Musicvolume insted
}
 
//////////////////////////////////////////////////////////////////////
bool CSoundSystem::IsEAX(void)
{
  bool tempB=false;
  ptParamBOOL boolParam(tempB);
  m_pAudioDevice->GetParam(adpEAX_STATUS, &boolParam);
  boolParam.GetValue(tempB);
  return tempB;
}

//////////////////////////////////////////////////////////////////////////
void CSoundSystem::SetMasterVolume(float fVol)
{
	if (!NumberValid(fVol))
	{
		Log(eSLT_Warning, "<Sound> %s Invalid input values (possible NaNs) on SetMasterVolume()");
		assert(0);
		return;
	}

	g_SoundCVars.g_fGameMasterVolume = fVol;
	ptParamF32 NewParam(g_SoundCVars.g_fGameMasterVolume * m_fNoFocusVolume);
	m_pAudioDevice->SetParam(adpMASTER_VOLUME, &NewParam);

	// Update the single game volume members accordingly.
	// Don't touch the master volume members here!
	g_SoundCVars.g_fGameSFXVolume    = g_SoundCVars.g_fGameMasterVolume * m_fNoFocusVolume * m_fGameSFXRatio;
	g_SoundCVars.g_fGameMusicVolume  = g_SoundCVars.g_fGameMasterVolume * m_fNoFocusVolume * m_fGameMusicRatio;
	g_SoundCVars.g_fGameDialogVolume = g_SoundCVars.g_fGameMasterVolume * m_fNoFocusVolume * m_fGameDialogRatio;
}

//////////////////////////////////////////////////////////////////////
void CSoundSystem::SetMasterVolumeScale(float fScale, bool bForceRecalc)
{ 
	if (!NumberValid(fScale))
	{
		Log(eSLT_Warning, "<Sound> %s Invalid input values (possible NaNs) on SetMasterVolumeScale()");
		assert(0);
		return;
	}

	//GUARD_HEAP;
	if ((!bForceRecalc) && ((fabsf(fScale-m_fSoundScale)) < 0.01f))
		return;

	m_fSoundScale = fScale;
}

//////////////////////////////////////////////////////////////////////
//! sets the weather condition that affect acoustics
bool CSoundSystem::SetWeatherCondition(float fWeatherTemperatureInCelsius, float fWeatherHumidityAsPercent, float fWeatherInversion)
{
	//GUARD_HEAP;
	
	bool localresult = true;
	if (fWeatherTemperatureInCelsius >= -273.15f && fWeatherTemperatureInCelsius <= 100.0f)
	{
		m_fWeatherTemperatureInCelsius = fWeatherTemperatureInCelsius;
	}
	else 
	{
		if (fWeatherTemperatureInCelsius < -273.15f) m_fWeatherTemperatureInCelsius = -273.15f;
		if (fWeatherTemperatureInCelsius > 100.0f) m_fWeatherTemperatureInCelsius = 100.0;
		localresult = false;
	}

	if (fWeatherHumidityAsPercent >= 0.0f && fWeatherHumidityAsPercent <= 100.0f)
	{
		m_fWeatherHumidityAsPercent = m_fWeatherHumidityAsPercent;
	}
	else 
	{
		if (fWeatherHumidityAsPercent < 0.0f) m_fWeatherHumidityAsPercent = 0;
		if (fWeatherHumidityAsPercent > 100.0f) m_fWeatherHumidityAsPercent = 100.0f;
		localresult = false;
	}

	if (fWeatherInversion >= 0.0f && fWeatherInversion <= 1.0f)
	{
		m_fWeatherInversion = fWeatherInversion;
	}
	else 
	{
		if (m_fWeatherInversion < 0.0f) m_fWeatherInversion = 0.0f;
		if (m_fWeatherInversion > 1.0f) m_fWeatherInversion = 1.0f;
		localresult = false;
	}

	// Calculation of the Airabsorptionmultiplyer
	float RelativeAirPressure = 1;
	float TemperatureInKelvin = m_fWeatherTemperatureInCelsius + 273.15f;
	float RelativeTemperature = TemperatureInKelvin/293.15f;  // convert to relative air temp (re 20 deg C)
	float Frequency2 = 2500.0f * 2500.0f;
	float C = 4.6151f - 6.8346f * cry_powf((273.16f/TemperatureInKelvin),1.261f);
	float h = m_fWeatherHumidityAsPercent*cry_powf(10,C)*RelativeAirPressure;	
	
	float FrequencyRelaxiationOfOxygen = RelativeAirPressure*(24.0f + 4.04e4f*h*(0.02f + h) / (0.391f + h));
	float FrequencyRelaxiationOfNitrogen=RelativeAirPressure*cry_powf(RelativeTemperature,-0.5f)*(9.0f+280.0f*h*cry_expf(-4.17f*(cry_powf(RelativeTemperature,(-1.0f/3.0f))-1.0f)));

	float TempResult1 = 0.01275f*(cry_expf(-2239.1f/TemperatureInKelvin)*1.0f
						/ (FrequencyRelaxiationOfOxygen + Frequency2/FrequencyRelaxiationOfOxygen));

	float TempResult2 = 0.1068f * (cry_expf(-3352.0f/TemperatureInKelvin)* 1.0f
						/(FrequencyRelaxiationOfNitrogen+Frequency2 / FrequencyRelaxiationOfNitrogen));

	float alpha = 8.686f*Frequency2*(1.84e-11f*(1.0f/RelativeAirPressure)*sqrtf(RelativeTemperature) 
				  + cry_powf(RelativeTemperature,-2.5f)* (TempResult1 + TempResult2));

	//float dBAttenuationPerKilometer = 1000*alpha;
	//float mdBAttenuationPerMeter = -100 * alpha; // EAX wants negative attenuation in mdB = 1/100th dB

	// TODO Get Airabsorption from ReverbManager
	//if (mdBAttenuationPerMeter > 0.0001) m_fWeatherAirAbsorptionMultiplyer=0;
	//else m_fWeatherAirAbsorptionMultiplyer = mdBAttenuationPerMeter/m_sTempEaxPreset.EAX.AirAbsorptionHF;

	return localresult;
}

//////////////////////////////////////////////////////////////////////
//! gets the weather condition that affect acoustics
bool CSoundSystem::GetWeatherCondition(float &fWeatherTemperatureInCelsius, float &fWeatherHumidityAsPercent, float &fWeatherInversion)
{
	//GUARD_HEAP;
	fWeatherTemperatureInCelsius = m_fWeatherTemperatureInCelsius;
	fWeatherHumidityAsPercent = m_fWeatherHumidityAsPercent;
	fWeatherInversion = m_fWeatherInversion;
	return true;
}

//////////////////////////////////////////////////////////////////////
void	CSoundSystem::GetMemoryInfo(SSoundMemoryInfo *pMemInfo) const
{
	// Bucket allocator pool info
	//pMemInfo->fSoundBucketPoolSizeInMB					= 0; needs implementation
	pMemInfo->fSoundBucketPoolUsedInMB					= g_MemoryPoolPrimary.GetSmallAllocsSize()/1048576.0f;
	pMemInfo->fSoundBucketPoolMaxInMB						= g_oSoundMemoryInfo.fSoundBucketPoolMaxInMB;
	pMemInfo->nSoundBucketPoolAllocCountMax			= g_oSoundMemoryInfo.nSoundBucketPoolAllocCountMax;

	// Primary pool info
	pMemInfo->fSoundPrimaryPoolSizeInMB					= g_MemoryPoolPrimary.MemSize()/1048576.0f;
	pMemInfo->fSoundPrimaryPoolUsedInMB					= pMemInfo->fSoundPrimaryPoolSizeInMB - g_MemoryPoolPrimary.MemFree()/1048576.0f;
	pMemInfo->fSoundPrimaryPoolMaxInMB					= g_oSoundMemoryInfo.fSoundPrimaryPoolMaxInMB;
	//pMemInfo->fSoundPrimaryPoolLargestBlockInMB	= 0;
	pMemInfo->nSoundPrimaryPoolAllocCountMax		= g_oSoundMemoryInfo.nSoundPrimaryPoolAllocCountMax;

	// Secondary pool info
#ifdef PROVIDE_SECONDARY_POOL
	pMemInfo->fSoundSecondaryPoolSizeInMB				= g_MemoryPoolSoundSecondary.MemSize()/1048576.0f;
	pMemInfo->fSoundSecondaryPoolUsedInMB				= pMemInfo->fSoundSecondaryPoolSizeInMB - g_MemoryPoolSoundSecondary.MemFree()/1048576.0f;
#endif
	pMemInfo->fSoundSecondaryPoolMaxInMB				= g_oSoundMemoryInfo.fSoundSecondaryPoolMaxInMB;
	pMemInfo->nSoundSecondaryPoolAllocCountMax	= g_oSoundMemoryInfo.nSoundSecondaryPoolAllocCountMax;

	// Miscellaneous info
	pMemInfo->nBufferTrashedCount								= CSoundSystem::g_nBuffersTrashed;
	pMemInfo->nMemTypeNormalCount								= g_oSoundMemoryInfo.nMemTypeNormalCount;
	pMemInfo->nMemTypeStreamFileCount						= g_oSoundMemoryInfo.nMemTypeStreamFileCount;
	pMemInfo->nMemTypeStreamDecodeCount					= g_oSoundMemoryInfo.nMemTypeStreamDecodeCount;
	pMemInfo->nMemTypeXBox360PhysicalCount			= g_oSoundMemoryInfo.nMemTypeXBox360PhysicalCount;
	pMemInfo->nMemTypePersistentCount						= g_oSoundMemoryInfo.nMemTypePersistentCount;
	pMemInfo->nMemTypeSecondaryCount						= g_oSoundMemoryInfo.nMemTypeSecondaryCount;

	for (size_t i = 0; i < SSoundMemoryInfo::eAllocationsArraySize; ++i)
		pMemInfo->anAllocations[i] = g_oSoundMemoryInfo.anAllocations[i];
}

//////////////////////////////////////////////////////////////////////////
int	CSoundSystem::GetUsedVoices() const
{
	//GUARD_HEAP;
  int nUsedVoices = 0;

  ptParamINT32 TempParam(nUsedVoices);

  // actively played sounds
  if (m_pAudioDevice->GetParam(adpCHANNELS_PLAYING ,&TempParam))
	{
		TempParam.GetValue(nUsedVoices);
	}
	else
	{
		// software fall back
		nUsedVoices = 0;
		TSoundIDSet::const_iterator ItEnd = m_currentSoundIDs.end();
		for (TSoundIDSet::const_iterator It=m_currentSoundIDs.begin(); It!=ItEnd; ++It)
		{
			CSound* const pSound = (CSound* const)(GetSound(*It));

			if (pSound)
			{
				if (pSound->IsPlayingOnChannel())
					++nUsedVoices;
			}
		}
	}

  return nUsedVoices;
}

///////////////////////////////////	///////////////////////////////////////
float	CSoundSystem::GetCPUUsage() const
{
	//GUARD_HEAP;
  float fCpuPctUsed = 0.0f;

  m_pAudioDevice->GetCpuUsage(0, 0, 0, 0, &fCpuPctUsed);

  return fCpuPctUsed;
}

//////////////////////////////////////////////////////////////////////////

float CSoundSystem::GetMusicVolume() const
{
	return (m_fMusicVolume * m_fMusicEffectVolume * m_fGameMusicVolume);
}

//////////////////////////////////////////////////////////////////////////
void CSoundSystem::CalcDirectionalAttenuation(const Vec3 &vPos, const Vec3 &vDir, const float fConeInRadians)
{
	//GUARD_HEAP;
	float fCosCone = (float)cry_cosf(fConeInRadians);
	if (IsEquivalent(vPos, m_DirAttPos, VEC_EPSILON) &&
			IsEquivalent(vDir, m_DirAttDir, VEC_EPSILON) &&
			(fCosCone == m_fDirAttCone))
	 return;

	m_DirAttPos       = vPos;
	m_DirAttDir       = vDir;
	m_fDirAttCone     = fCosCone;
	m_fDirAttMaxScale = 0.0f;
	m_bNeedSoundZoomUpdate = true;

	// TODO remove the whole for loop when Update() of sound takes care of this automatically
	//update position of all looping/playing sounds
	//for (SoundSetIter It=m_setAllSounds.begin(); It!=m_setAllSounds.end(); ++It)
	//{
	//	CSound *cs = (*It);
	//	if (cs->IsPlaying() || cs->IsPlayingVirtual())
	//		cs->SetPosition();
	//}
}

//////////////////////////////////////////////////////////////////////////
void CSoundSystem::GetMemoryUsage(class ICrySizer* pSizer) const
{
	//GUARD_HEAP;
	if(pSizer==NULL)
		return;
	

#ifndef _LIB // Only when compiling as dynamic library
	{
		//SIZER_COMPONENT_NAME(pSizer,"Strings");
		//pSizer->AddObject( (this+1),string::_usedMemory(0) );
	}
	{
		SIZER_COMPONENT_NAME(pSizer,"STL Allocator Waste");
		CryModuleMemoryInfo meminfo;
		ZeroStruct(meminfo);
		CryGetMemoryInfoForModule( &meminfo );
		pSizer->AddObject( (this+2),(size_t)meminfo.STL_wasted );
	}
#endif

	{
		SIZER_COMPONENT_NAME(pSizer, "System");
		pSizer->AddObject( this, sizeof(*this));
	}
	
	pSizer->AddObject(m_DelayedPrecaches);
	pSizer->AddObject(m_AllSoundsArray);					
	pSizer->AddObject(m_pSoundAssetManager);	
	pSizer->AddObject(m_InactiveSounds);	
	pSizer->AddObject(m_UnusedSoundObjects);	
	pSizer->AddObject(m_pMoodManager);			
	pSizer->AddObject(m_pReverbManager);
	pSizer->AddObject(m_pAudioDevice);
	pSizer->AddObject(m_SoundNameCacheCRC);

	stl::STLPoolAllocator<tSoundID, stl::PoolAllocatorSynchronizationSinglethreaded>::GetMemoryUsage(pSizer);		
}


//////////////////////////////////////////////////////////////////////////
int CSoundSystem::GetMemoryUsageInMB(bool bGetPoolSize)
{
	if (bGetPoolSize) {
		ICVar* pCacheStatic = gEnv->pConsole->GetCVar("s_CacheStatic");
		ICVar* pCacheSize = gEnv->pConsole->GetCVar("s_CacheSize");

		if (pCacheStatic && pCacheSize && pCacheStatic->GetIVal() == 1 )
		{
			return (int)pCacheSize->GetFVal();
		}

		return 0;


	} else {
		//GUARD_HEAP;
		int nSizeInByte = 0;

		nSizeInByte += m_pSoundAssetManager->GetMemUsage();

		nSizeInByte += m_pAudioDevice->GetMemoryUsage(0);


		int nSoundSize = 0;
		TSoundIDSet::const_iterator ItEnd = m_currentSoundIDs.end();
		for (TSoundIDSet::const_iterator It=m_currentSoundIDs.begin(); It!=ItEnd; ++It)
		{			
			nSoundSize += sizeof(CSound);
		}
		nSizeInByte += nSoundSize;

		nSizeInByte = nSizeInByte/(1024*1024);

		return nSizeInByte;
	}
}

//////////////////////////////////////////////////////////////////////////
int CSoundSystem::SetMinSoundPriority( int nPriority )
{
	int nPrev = m_nMinSoundPriority;
	m_nMinSoundPriority = nPriority;
	return nPrev;
}

//////////////////////////////////////////////////////////////////////////
void CSoundSystem::LockResources()
{
	m_pSoundAssetManager->LockResources();
}

//////////////////////////////////////////////////////////////////////////
void CSoundSystem::UnlockResources()
{
	m_pSoundAssetManager->UnlockResources();
}

////////////////////////////////////////////////////////////////////////////
void CSoundSystem::SetGlobalParameter( const char *sGlobalParameter, const float fValue)
{
	int nIndex = -1;
	m_ParameterNames.AddOrSetParameter(sGlobalParameter, true, fValue, nIndex);
}

////////////////////////////////////////////////////////////////////////////
void CSoundSystem::ProcessHDRS(const float fFrameTime)
{ 
#ifdef INCLUDE_AUDIO_PRODUCTION_CODE
	float fTextPosX = 50.f;
	float fTextPosY = 50.f;
#endif // INCLUDE_AUDIO_PRODUCTION_CODE

	IGame* pGame = gEnv->pGame;
	if(pGame)
	{
		IGameFramework *pFramework = pGame->GetIGameFramework();
		if(pFramework)
		{
			IEntity const* const pPlayerEntity = gEnv->pEntitySystem->GetEntity(pFramework->GetClientActorId());

			if (pPlayerEntity)
			{
				IListener const* const pListener = GetListener(GetClosestActiveListener(pPlayerEntity->GetWorldPos()));

				if (pListener)
				{
					const Vec3 v3ListenerPos = pListener->GetPosition();
					const Vec3* pListenerPos = &v3ListenerPos;

					//Looping sounds HDR is re-updated each frame
					//NOTE: All sounds UpdateHDRS in CSound::StartPlayback
					UpdateLoopedHDRS(pListenerPos);

					//Starts fading if loudness if lower than this + fLoudnessCutoff
					float fLoudnessFadeBand = g_fHDRFade;

					//Faded to zero if loudness is lower than this
					float fLoudnessCutoff = m_fLoudness - g_fHDRRange - g_fHDRFade;

					ApplyHDRS(pListenerPos, fLoudnessFadeBand, fLoudnessCutoff);

#ifdef INCLUDE_AUDIO_PRODUCTION_CODE
					if(g_nHDRDebug)
					{
						DrawLoudnessInformation(fTextPosX, fTextPosY, fLoudnessFadeBand + fLoudnessCutoff, fLoudnessCutoff);
						DrawHDRInformation(fTextPosX, fTextPosY, pListenerPos);
						DrawHDRSGraph();
					}
					else if(m_pHDRDebugHistory)
					{
						m_pHDRDebugHistory->Release();
						m_pHDRDebugHistory = NULL;
					}
#endif // INCLUDE_AUDIO_PRODUCTION_CODE

					//Lerp back towards zero
					m_fLoudness = max(0.0f, m_fLoudness + (g_fHDRFalloff * -m_fLoudness * fFrameTime));
				}
			}
		}
	}
}

////////////////////////////////////////////////////////////////////////////
void CSoundSystem::ApplyHDRS(const Vec3* pListenerPos, const float fLoudnessFadeBand, const float fLoudnessCutoff)
{
	FUNCTION_PROFILER(GetISystem(), PROFILE_SOUND);
	const float fInvHDRFade = 1.0f / g_fHDRFade;

	SSoundContainer::iterator LoopingEnd = m_LoopedSounds.end();
	for (SSoundContainer::iterator LoopingIt = m_LoopedSounds.begin(); LoopingIt != LoopingEnd; ++LoopingIt )
	{
		ISound* const pSound = *LoopingIt;
		ApplyHDRS( pSound, fInvHDRFade, pListenerPos, fLoudnessFadeBand, fLoudnessCutoff );
	}
	SSoundContainer::iterator OneShotEnd = m_OneShotSounds.end();
	for (SSoundContainer::iterator OneShotIt = m_OneShotSounds.begin(); OneShotIt != OneShotEnd; ++OneShotIt )
	{
		ISound* const pSound = *OneShotIt;
		ApplyHDRS( pSound, fInvHDRFade, pListenerPos, fLoudnessFadeBand, fLoudnessCutoff );
	}
}

////////////////////////////////////////////////////////////////////////////
void CSoundSystem::ApplyHDRS( ISound* const pSound, const float fInvHDRFade, const Vec3* pListenerPos, const float fLoudnessFadeBand, const float fLoudnessCutoff )
{
	if ( ShouldHDRS(pSound) )
	{
		float fLoudness = 0.0f;
		if(GetLoudness(pSound, pListenerPos, fLoudness))
		{
			float fHDRA = 1.0f;
			fLoudness -= fLoudnessCutoff;
			fLoudness = static_cast<float>( __fsel( fLoudness, fLoudness, 0.0f ) );
			fHDRA = static_cast<float>( __fsel( fLoudness - fLoudnessFadeBand, 1.0f, fLoudness * fInvHDRFade ) );

			CSound* const pCSound = (CSound* const)(pSound);
			pCSound->SetHDRA(fHDRA);
		}
	}
}

////////////////////////////////////////////////////////////////////////////
bool CSoundSystem::GetLoudness(ISound* pSound, const Vec3* pListenerPos, float &fLoudness)
{
	CSound* const pCSound = (CSound* const)(pSound);
	bool hasLoudness = pCSound->GetPlatformSoundPtr()->GetLoudnessProperty(fLoudness);
	if (hasLoudness && fLoudness > 0.0f) //ignore anything with a loudness of zero
	{
		if((pCSound->GetFlags() & FLAG_SOUND_3D) && pListenerPos)
		{
			float distSq = pListenerPos->GetSquaredDistance(pCSound->GetPlaybackPosition());
			fLoudness = max(fLoudness - (distSq * g_fHDRLoudnessFalloff), max(fLoudness - g_fHDRLoudnessMaxFalloff, 0.1f));
		}

		return true;
	}

	return false;
}

////////////////////////////////////////////////////////////////////////////
void CSoundSystem::UpdateLoopedHDRS(const Vec3* pListenerPos)
{
	SSoundContainer::const_iterator itLoopedEnd = m_LoopedSounds.end();
	for (SSoundContainer::const_iterator itLooped = m_LoopedSounds.begin(); itLooped != itLoopedEnd; ++itLooped)
	{
		ISound *pSound = (*itLooped);
		if (pSound)
		{
			UpdateHDRS(pSound, pListenerPos);
		}
	}

}

////////////////////////////////////////////////////////////////////////////
void CSoundSystem::UpdateHDRS(ISound* pSound)
{
	IGame* pIGame = gEnv->pGame;
	if (pIGame==0)
		return; //in Sandbox we don't always have a Game.dll
	IEntity const* const pPlayerEntity = gEnv->pEntitySystem->GetEntity(pIGame->GetIGameFramework()->GetClientActorId());

	if (pPlayerEntity)
	{
		IListener const* const pListener = GetListener(GetClosestActiveListener(pPlayerEntity->GetWorldPos()));

		if (pListener)
		{
			const Vec3 v3ListenerPos = pListener ? pListener->GetPosition() : Vec3();
			const Vec3* pListenerPos = pListener ? &v3ListenerPos : NULL;

			UpdateHDRS(pSound, pListenerPos);
		}
	}
}

////////////////////////////////////////////////////////////////////////////
void CSoundSystem::UpdateHDRS(ISound* pSound, const Vec3* pListenerPos)
{
	assert(g_nHDR == 1);

	if(ShouldHDRS(pSound))
	{
		float fLoudness = 0.0f;
		if(GetLoudness(pSound, pListenerPos, fLoudness))
		{
			m_fLoudness = max(m_fLoudness, fLoudness);

#ifdef INCLUDE_AUDIO_PRODUCTION_CODE
			if(m_fLoudness == fLoudness)
			{
				m_nLoudestSoundId = pSound->GetId();
			}
#endif // INCLUDE_AUDIO_PRODUCTION_CODE
		}
	}
}

#ifdef INCLUDE_AUDIO_PRODUCTION_CODE
////////////////////////////////////////////////////////////////////////////
void CSoundSystem::DrawLoudnessInformation( float &fTextPosX, float &fTextPosY, const float fFade, const float fCutoff )
{
	const float fFontSize = 1.4f;
	const float fStepY = 15.0f;

	float fColorGreen[4]	={0.0f, 1.0f, 0.0f, 1.0f};

	gEnv->pRenderer->Draw2dLabel(fTextPosX, fTextPosY, fFontSize, fColorGreen, false, string().Format("Loudness %.2f", m_fLoudness).c_str());
	fTextPosY += fStepY;
	gEnv->pRenderer->Draw2dLabel(fTextPosX, fTextPosY, fFontSize, fColorGreen, false, string().Format("    Fade %.2f", fFade).c_str());
	fTextPosY += fStepY;
	gEnv->pRenderer->Draw2dLabel(fTextPosX, fTextPosY, fFontSize, fColorGreen, false, string().Format("    Cutoff %.2f", fCutoff).c_str());
	fTextPosY += fStepY;
	if(m_fLoudness > 0.0f && m_nLoudestSoundId != INVALID_SOUNDID)
	{
		ISound* pSound = GetSound(m_nLoudestSoundId);
		if(pSound)
		{
			gEnv->pRenderer->Draw2dLabel(fTextPosX, fTextPosY, fFontSize, fColorGreen, false, string().Format("        %s",pSound->GetName()).c_str());
			fTextPosY += fStepY;
		}
	}
	fTextPosY += fStepY;
}

////////////////////////////////////////////////////////////////////////////
void CSoundSystem::DrawHDRInformation(float &fTextPosX, float &fTextPosY, const Vec3* pListenerPos)
{
	SSoundContainer::iterator LoopingEnd = m_LoopedSounds.end();
	for (SSoundContainer::iterator LoopingIt = m_LoopedSounds.begin(); LoopingIt != LoopingEnd; ++LoopingIt )
	{
		ISound* const pSound = (*LoopingIt);
		if (ShouldHDRS(pSound))
		{
			float fLoudness = 0.0f;
			if(GetLoudness(pSound, pListenerPos, fLoudness))
			{
				CSound* const pCSound = (CSound* const)(pSound);
				DrawHDRSoundInformation(fTextPosX, fTextPosY, pSound, fLoudness, pCSound->GetHDRA());
			}
		}
	}

	SSoundContainer::iterator OneShotEnd = m_OneShotSounds.end();
	for (SSoundContainer::iterator OneShotIt = m_OneShotSounds.begin(); OneShotIt != OneShotEnd; ++OneShotIt )
	{
		ISound* const pSound = (*OneShotIt);
		if (ShouldHDRS(pSound))
		{
			float fLoudness = 0.0f;
			if(GetLoudness(pSound, pListenerPos, fLoudness))
			{
				CSound* const pCSound = (CSound* const)(pSound);
				DrawHDRSoundInformation(fTextPosX, fTextPosY, pSound, fLoudness, pCSound->GetHDRA());
			}
		}
	}
}

////////////////////////////////////////////////////////////////////////////
void CSoundSystem::DrawHDRSoundInformation( float &fTextPosX, float &fTextPosY, ISound* pSound, const float fLoudness, const float fHDRA )
{
	if(strstr(pSound->GetName(), "%") == NULL)
	{
		const float fFontSize = 1.4f;
		const float fStepY = 15.0f;

		float fColor[4]	={1.0f - fHDRA, fHDRA, 0.0f, 1.0f};
		gEnv->pRenderer->Draw2dLabel(fTextPosX, fTextPosY, fFontSize, fColor, false, string().Format("%s [%s] - (%.2f) %.2f", pSound->GetName(), (pSound->GetFlags() & FLAG_SOUND_3D)? "3D" : "2D", fLoudness, fHDRA).c_str());
		fTextPosY += fStepY;
	}
}

////////////////////////////////////////////////////////////////////////////
void CSoundSystem::DrawHDRSGraph()
{
	if(m_pHDRDebugHistory == NULL)
	{
		m_pHDRDebugHistory = gEnv->pGame->GetIGameFramework()->CreateDebugHistoryManager();

		const float fMinOut = -20.0f;
		const float fMaxOut = 20.0f;
		const float fMinIn = 0.0f;
		const float fMaxIn = 1.0f;
		const float fX = 3.0f;
		const float fY = 1.0f;
		const float fW = 2.5f;
		const float fH = 1.0f;

		m_pHDRDebugHistory->LayoutHelper("HDRSLoudness", NULL, true, fMinOut, fMaxOut, fMinIn, fMaxIn, fX, fY, fW, fH);
		m_pHDRDebugHistory->LayoutHelper("HDRSFade", NULL, true, fMinOut, fMaxOut, fMinIn, fMaxIn, fX, fY, fW, fH);
		m_pHDRDebugHistory->LayoutHelper("HDRSCutoff", NULL, true, fMinOut, fMaxOut, fMinIn, fMaxIn, fX, fY, fW, fH);

		const ColorF green(0.0f, 1.0f, 0.0f, 1.0f);
		const ColorF orange(1.0f, 0.5f, 0.0f, 1.0f);
		const ColorF red(1.0f, 0.0f, 0.0f, 1.0f);
		const ColorF colorBox(0.2f, 0.2f, 0.2f, 0.2f);
		const ColorF colorGridLine(0.0f, 0.0f, 1.0f, 0.2f);
		const ColorF invisible(0.0f, 0.0f, 0.0f, 0.0f);

		const int nGridLineCount = 3;

		IDebugHistory* pDHLoudness = m_pHDRDebugHistory->GetHistory("HDRSLoudness");
		if (pDHLoudness != NULL)
		{
			pDHLoudness->SetupColors(green, green, colorBox, colorGridLine, invisible, invisible);
			pDHLoudness->SetGridlineCount(nGridLineCount, nGridLineCount);
		}
		IDebugHistory* pDHFade = m_pHDRDebugHistory->GetHistory("HDRSFade");
		if (pDHFade != NULL)
		{
			pDHFade->SetupColors(orange, orange, colorBox, colorGridLine, invisible, invisible);
			pDHFade->SetGridlineCount(nGridLineCount, nGridLineCount);
		}
		IDebugHistory* pDHCutoff = m_pHDRDebugHistory->GetHistory("HDRSCutoff");
		if (pDHCutoff != NULL)
		{
			pDHCutoff->SetupColors(red, red, colorBox, colorGridLine, invisible, invisible);
			pDHCutoff->SetGridlineCount(nGridLineCount, nGridLineCount);
		}
	}
	else
	{
		IDebugHistory* pDHLoudness = m_pHDRDebugHistory->GetHistory("HDRSLoudness");
		if (pDHLoudness != NULL)
		{
			pDHLoudness->AddValue(m_fLoudness);
		}
		IDebugHistory* pDHFade = m_pHDRDebugHistory->GetHistory("HDRSFade");
		if (pDHFade != NULL)
		{
			pDHFade->AddValue(max(m_fLoudness - g_fHDRFade, 0.0f));
		}
		IDebugHistory* pDHCutoff = m_pHDRDebugHistory->GetHistory("HDRSCutoff");
		if (pDHCutoff != NULL)
		{
			pDHCutoff->AddValue(max(m_fLoudness - g_fHDRRange - g_fHDRFade, 0.0f));
		}
	}
}
#endif // INCLUDE_AUDIO_PRODUCTION_CODE

////////////////////////////////////////////////////////////////////////////
void CSoundSystem::ProcessInactiveSounds(const ESoundUpdateMode UpdateMode)
{
	FRAME_PROFILER( "SoundSystem::Update:ProcessInactive", gEnv->pSystem, PROFILE_SOUND );
	float fCurrTime = gEnv->pTimer->GetCurrTime(); // store the current time locally

	int nActiveSize = m_OneShotSounds.size(); // size of active sound spots
	if ((nActiveSize < g_nMaxActiveSounds) && (!m_InactiveSounds.empty()))
	{		
		int nInactiveSounds = m_InactiveSounds.size();

		// lets choose the iteration-count, so it iterates through all sounds every m_pCVarInactiveSoundIterationTimeout seconds
		int nInactiveSoundItCount = (int)cry_ceilf((gEnv->pTimer->GetFrameTime()/g_fInactiveSoundIterationTimeout) * (float)nInactiveSounds);	

		if (UpdateMode & eSoundUpdateMode_CleanInactive)
			nInactiveSoundItCount = nInactiveSounds;

		// make sure we at least process one
		nInactiveSoundItCount = max(nInactiveSoundItCount, 1);
		int nSoundSpotAdded = 0;

		SSoundContainer::iterator It = m_InactiveSounds.begin();
		SSoundContainer::iterator ItEnd = m_InactiveSounds.end();

		// continue where we stopped last time if index is within the range
		if (nInactiveSounds > m_nLastInactiveSoundsIndex)
			It += m_nLastInactiveSoundsIndex;
		else
			m_nLastInactiveSoundsIndex = 0;

		for (int i=0; i < nInactiveSoundItCount && !m_InactiveSounds.empty(); ++i)
		{
			bool bActivateSound = false;
			bool bDeactivateSound = false;
			bool bTooFar = false;
			
			CSound* const pSound = (CSound* const)((*It).get());

			const bool bIsActive = pSound->IsActive();

			// remove stopped Sound as quickly as we can
			if (pSound->GetState() == eSoundState_Stopped && !bIsActive)
				bDeactivateSound = true;

			// dont trust values if the sound is not initialized, but lets stop an uninitialized sound
			if (!pSound->IsInitialized() && (pSound->GetState() != eSoundState_Stopped))
			{
				// try to trigger that the sounds gets initialized
				if (pSound->GetSoundBufferPtr())
				{
					if (!pSound->GetSoundBufferPtr()->Loading() && !pSound->GetSoundBufferPtr()->Loaded(false))
					{
						bool bCanBeLoaded = pSound->GetSoundBufferPtr()->Load(pSound);

						if (pSound->GetSoundBufferPtr()->LoadFailure())
							pSound->Stop(ESoundStopMode_AtOnce);
					}
				}
			}
			else
			{
				// process the sound

				bool bIsSoundPH = IsSoundPH(pSound);


				// catch oneshots which got stolen or somehow ended playing but aren't actually
				if (!pSound->GetLoopMode() && !bIsActive && !pSound->GetPaused() && !pSound->IsLoading())
				{
					// catch sounds which are not updated through looping or oneshot lists
					if (!pSound->IsPlaying())
						pSound->Update();
					else
						pSound->Stop();
				}

				// catch loops which somehow ended playing but aren't actually
				if (!bDeactivateSound && pSound->GetLoopMode() && pSound->GetState() != eSoundState_Inactive)
				{
					// catch sounds which are not updated through looping or oneshot lists
					pSound->Update();

					if (m_bSoundSystemPause)
					{
						// Make sure to pause these sounds in case the SoundSystem is paused.
						pSound->SetPaused(true);
					}
				}

				if ((pSound->GetFlags() & FLAG_SOUND_RADIUS))
				{
					if (m_bNeedSoundZoomUpdate)
						pSound->SetPosition(pSound->GetPosition());
				}

				if (((pSound->IsPlaying() && !pSound->GetPaused()) || pSound->GetFlags() & FLAG_SOUND_RELATIVE) && !pSound->IsLoading() && bIsSoundPH)
				{
					IListener* pListener = GetListener(GetClosestActiveListener(pSound->GetPlaybackPosition()));
					if (!pListener)
						return;
					float fDist2 = pListener->GetPosition().GetSquaredDistance(pSound->GetPlaybackPosition());

					if (fDist2 <= pSound->GetSquardedPreloadRadius() || !(pSound->GetFlags() & FLAG_SOUND_RADIUS) ) // Prevent sounds without a radius from being taken into account here (this caused CRYII-2268)
					{					
						//////////////////////////////////////////////////////////////////////////					
						// are we getting too many sound spots?					
						if ((nActiveSize+nSoundSpotAdded) > g_nMaxActiveSounds)
						{
							break; // resume next frame
						}
						//////////////////////////////////////////////////////////////////////////

						bActivateSound = true;
					}
					else 
					{
						// remove all non-looping sounds that are expired
						if (!bDeactivateSound && !pSound->GetLoopMode())
						{
							if ((pSound->IsPlayLengthExpired(fCurrTime) && !pSound->IsActive()) || pSound->GetState() == eSoundState_Stopped)
								bDeactivateSound = true;
						}					
						else
							bTooFar = true;
					}
				}
				else
				{
					if (!bDeactivateSound && ((!pSound->IsPlaying() && !pSound->IsLoading()) || (!bIsSoundPH && !pSound->GetLoopMode())) && !pSound->IsActive() )
					{
						if (pSound->IsPlaying())
							pSound->Update();

						// Only deactivate non-looping sounds or every sound that is stopped
						// Sounds that are  inactive might very well be pre-cached sounds waiting to be triggered
						if (pSound->GetState() != eSoundState_Inactive && (!pSound->GetLoopMode() || pSound->GetState() == eSoundState_Stopped))
							bDeactivateSound = true;
					}
				}

				if (bActivateSound)
				{					
					int nActiveSounds = (int)m_OneShotSounds.size() + (int)m_LoopedSounds.size();
					bool bTooManyActiveSounds = (nActiveSounds > g_nMaxActiveSounds);

					if (!bTooManyActiveSounds)
					{
						// move sound to active list...
						if (pSound->GetLoopMode())
							m_LoopedSounds.AddUnique(pSound);
						else
							m_OneShotSounds.AddUnique(pSound);

						nSoundSpotAdded++;

						// remove it: copy last element (which was not processed) and pop back
						if (It != ItEnd-1)
						{
							(*It) = *(ItEnd-1);
						}
						else
						{
							// wrap by pop_back
							It = m_InactiveSounds.begin();
							m_nLastInactiveSoundsIndex = 0;
						}

						pSound->SetState(eSoundState_Active);
						pSound->Preload();			

						m_InactiveSounds.pop_back();
						ItEnd = m_InactiveSounds.end(); // renew ItEnd

						continue; // It already points to next unprocessed, dont ++It
					}
				}

				if (bDeactivateSound)
				{
					pSound->SetState(eSoundState_None);

					assert(!pSound->IsActive());

					// pass it over
					m_stoppedSoundToBeDeleted.AddUnique(pSound); 

					if (!pSound->GetLoopMode() && pSound->IsPlaying())
						pSound->Stop();

					// remove it: copy last element and pop back
					if (It != ItEnd-1)
					{
						(*It) = *(ItEnd-1);
					}
					else
					{
						// wrap by pop_back
						It = m_InactiveSounds.begin();
						m_nLastInactiveSoundsIndex = 0;
					}

					m_InactiveSounds.pop_back();
					ItEnd = m_InactiveSounds.end(); // renew ItEnd
					continue; // It already points to next unprocessed, dont ++It
				}

				// sound could also be just created but not started yet
				if (!bActivateSound && !bDeactivateSound && !bTooFar && (pSound->GetState() != eSoundState_Inactive))
				{
					// not sure what happened here, stuck in obstruction?
					if ((pSound->GetFlags() & FLAG_SOUND_OBSTRUCTION) && (g_nObstructionAccuracy > 0) && pSound->GetObstruction() && (pSound->GetObstruction()->nRaysShot == 0))
					{
						if (pSound->GetObstruction()->bDelayPlayback)
							pSound->Update();
						else
							pSound->Play();
					}
				}
			}


			// next unprocessed element
			++It;
			++m_nLastInactiveSoundsIndex;

			// if current It is the last sound spot then reset It to begin
			if (It == ItEnd)
			{
				It = m_InactiveSounds.begin();
				m_nLastInactiveSoundsIndex = 0;
			}
		} // for loop
	}
}

//////////////////////////////////////////////////////////////////////////
void CSoundSystem::ProcessOneShotSounds()
{
	FRAME_PROFILER( "SoundSystem::Update:ProcessOneShot", gEnv->pSystem, PROFILE_SOUND );
	//////////////////////////////////////////////////////////////////////////
	// Stop expired auto stop sounds.
	//////////////////////////////////////////////////////////////////////////
	if (!m_OneShotSounds.empty())
	{
		float fCurrTime = gEnv->pTimer->GetCurrTime(); // store the current time locally

		//Log(eSLT_Message, "<Sound> Processing OneShotSounds: %d\n", m_OneShotSounds.size());
		//float fCurrTime = gEnv->pTimer->GetCurrTime(); // store the current time locally
		SSoundContainer::iterator ItEnd = m_OneShotSounds.end();
		for (SSoundContainer::iterator It = m_OneShotSounds.begin(); It != ItEnd; )
		{
			CSound* const pSound = (CSound* const)((*It).get());

			if ((pSound->GetFlags() & FLAG_SOUND_RADIUS))
			{
				if (m_bNeedSoundZoomUpdate)
					pSound->SetPosition(pSound->GetPosition());
			}

			bool bIsSoundPotentiallyHearable = ((pSound->GetFlags() & FLAG_SOUND_2D) || IsSoundPH(pSound));
			if (bIsSoundPotentiallyHearable)
			{
				pSound->SetFade(1.0f, 1000);
			}
			else
			{
				pSound->SetFade(0.0f, 1000);
			}

			const bool bIsEnding = pSound->GetState() == eSoundState_Ending;
			bool bStopped = pSound->GetState() == eSoundState_Stopped;
			const bool bNoHandle = pSound->GetPlatformSoundPtr()->GetSoundHandle() == NULL;
			const bool bIsPlaying = (pSound->IsPlaying() && !bStopped); // timed out or ended by callback
			const bool bProcessSound = ProcessActiveSound(pSound);
			const bool bFadeState = pSound->GetFadeState() == eFadeState_JustFinished;
			
			// This sound plays, but has not handle. It got stolen or failed to spawn, so lets end it
			if (bIsPlaying && bNoHandle && bProcessSound)
			{
				pSound->Stop();
				bStopped = pSound->GetState() == eSoundState_Stopped;
			}

			if ((!(bIsPlaying && bProcessSound)) || (!bIsSoundPotentiallyHearable && bFadeState) || bIsEnding)// || bExpired) // && pSound->IsPlayingOnChannel()
			{

				if (!bIsEnding)
				{
					pSound->Stop(); 
				}
			}

			if (bStopped)
			{
				//CTimeValue test = gEnv->pTimer->GetFrameStartTime();
				//Log(eSLT_Message, "<Sound> Sound moved to ToBeDeleted: %s : %f\n", pSound->GetName(), test.GetSeconds());
				m_stoppedSoundToBeDeleted.AddUnique( pSound );

				// Copy last element (not processed) and pop back
				if (It != ItEnd-1)
				{
					(*It) = *(ItEnd-1);
					m_OneShotSounds.pop_back();
					ItEnd = m_OneShotSounds.end();
					continue; // It already points to next unprocessed, don't ++It
				}
				else
				{
					m_OneShotSounds.pop_back();
					break;
				}
			}

			//if (pSound->GetState() == eSoundState_Active || pSound->GetState() == eSoundState_Ending)

			pSound->Update();

		// next unprocessed element
		++It;
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CSoundSystem::ProcessLoopedSounds()
{
	FRAME_PROFILER( "SoundSystem::Update:ProcessLooped", gEnv->pSystem, PROFILE_SOUND );
	// process all active looped sounds for fading and occlusion
	//float fCurrTime = gEnv->pTimer->GetCurrTime(); // store the current time locally
	//std::pair< SoundSmartSetIter, bool > SoundSetIterBoolPair;

	SSoundContainer::iterator ItEnd = m_LoopedSounds.end();
	for (SSoundContainer::iterator It = m_LoopedSounds.begin(); It!=ItEnd; )
	{
//		FRAME_PROFILER( "SoundSystem::Update:ProcessActive",GetSystem(),PROFILE_SOUND );

		CSound* const pSound = (CSound* const)((*It).get());

		// if loading failed: set NONE state and remove it from active sound spots
		if (!pSound->GetSoundBufferPtr() || (pSound->GetSoundBufferPtr() && pSound->GetSoundBufferPtr()->LoadFailure()) || !pSound->IsPlaying())
		{
			pSound->Stop();
			m_stoppedSoundToBeDeleted.AddUnique(pSound);

			// Copy last element (not processed) and pop back
			if (It != ItEnd-1)
			{
				(*It) = *(ItEnd-1);
				m_LoopedSounds.pop_back();
				ItEnd = m_LoopedSounds.end();
				continue; // It already points to next unprocessed, don't ++It
			}
			else
			{
				m_LoopedSounds.pop_back();
				break;
			}
		}

		// check if the sound became occluded
		//bool bZeroFadeCheckDist = false;
		bool bDeactivate = false;

		bool bIsSoundPotentiallyHearable = IsSoundPH(pSound);
		if (bIsSoundPotentiallyHearable)
		{
			pSound->SetFade(1.0f, 1000);
		}
		else
		{
			pSound->SetFade(0.0f, 1000);
		}

		float fRatio				= 1.0f;
		float fDistSquared	= 0.0f;
		CListener* pListener = NULL;

		if ((pSound->GetFlags() & FLAG_SOUND_RADIUS))
		{
			pListener = (CListener*)GetListener(GetClosestActiveListener(pSound->GetPlaybackPosition()));

			if (pListener)
				fDistSquared = pListener->GetPosition().GetSquaredDistance(pSound->GetPlaybackPosition());

			if (m_bNeedSoundZoomUpdate)
				pSound->SetPosition(pSound->GetPosition());
		}

		float fDistanceToListener = sqrt_tpl(fDistSquared);
		bool bBeyondPreload = (fDistSquared > pSound->GetSquardedPreloadRadius());
		bool bBeyondRadius = (fDistSquared > pSound->GetSquardedMaxRadius());
		bool bMoveToInactive = false;

		if ( bBeyondPreload || (!bIsSoundPotentiallyHearable && pSound->GetFadeState() == eFadeState_JustFinished))
		{
			// The listener is beyond the sound's preload radius.
			if (!pSound->IsActive())
			{
				// If the playback ended move sound to inactive list.
				bMoveToInactive = true;
			}
		}
		else if (bBeyondRadius)
		{
			// The listener is beyond the sound's max radius.
			if (pSound->IsActive())
			{
				// End the playback and wait until the sound ended.
				pSound->SetRatio(0.0f);
				pSound->Deactivate();
				pSound->Update();
			}
		}
		
		if (bMoveToInactive && bBeyondPreload)
		{
			// move sound to inactive list...
			m_InactiveSounds.AddUnique(pSound);

			// copy last element (not processed) and pop back
			if (It != ItEnd-1)
			{
				(*It) = *(ItEnd-1);
			}
			m_LoopedSounds.pop_back();
			ItEnd = m_LoopedSounds.end(); // renew ItEnd

			pSound->SetState(eSoundState_Inactive);
			continue; // It already points to next unprocessed, dont ++It
		}

		if ((pSound->GetFlags() & FLAG_SOUND_RADIUS))
		{
			//calc attenuation, cal sound ratio between min & max radius
			fRatio = pSound->GetRatioByDistance(fDistanceToListener, false);
			pSound->SetRatio(fRatio);
		}

		if (pSound->GetSoundBufferPtr())
		{
			if (!pSound->GetSoundBufferPtr()->Loaded(false))
				pSound->GetSoundBufferPtr()->Load(pSound);
		}

		//if (pSound->GetState() == eSoundState_Active)
		pSound->Update();

		// if the sound is not allocated in the sound library yet and is a looping sound then trigger play event
		if (!pSound->IsActive() && pSound->IsPlayingVirtual() && bIsSoundPotentiallyHearable) //loop check 
		{
			if (!pListener)
			{
				int a = 0;	(void)a;
			}

			if (!pSound->CanBeCulled(pListener, fDistanceToListener, fDistSquared))
				pSound->StartPlayback(pListener, fDistanceToListener, fDistSquared);
		}

		// next unprocessed element
		++It;
	} // for loop AllActiveSounds
}

//////////////////////////////////////////////////////////////////////////
void CSoundSystem::ProcessPrecache()
{
	static float fTimeLeft = 0.0f;

	if (g_nPrecacheData && !m_DelayedPrecaches.empty() && !m_bDelayPrecaching)
	{
		int nOldLoadNonBlocking		= g_nLoadNonBlocking;
		g_nLoadNonBlocking				= 1;
		int nErrors								= 0;

		int nPrecacheSize							= m_DelayedPrecaches.size();
		float const fFrameTime				= gEnv->pTimer->GetFrameTime();
		static float fTempInterval		= 0.0f;
		fTimeLeft											-= fFrameTime;
		fTempInterval									+= fFrameTime;
		float const fUpdateInterval		= fTimeLeft / static_cast<float>(nPrecacheSize);

		// lets choose the iteration-count, so it iterates through all precaches within fTimeLeft seconds
		int nPrecacheCount = (int)cry_ceilf((gEnv->pTimer->GetFrameTime() / fTimeLeft) * (float)nPrecacheSize);	

		// make sure we have at least process one
		nPrecacheCount = max(nPrecacheCount, 1);

		if (fTempInterval > fUpdateInterval)
		{
			fTempInterval = 0.0f;

			SoundBufferPropsDelayedMapItor It = m_DelayedPrecaches.begin();
			SoundBufferPropsDelayedMapItorConst ItEnd		= m_DelayedPrecaches.end();

			// continue where we stopped last time if index is within the range
			if (nPrecacheSize > m_nLastPrecacheIndex)
				It += m_nLastPrecacheIndex;
			else
				m_nLastPrecacheIndex = 0;

			for (int i=0; i < nPrecacheCount; ++i)
			{
				{			
					SSoundBufferProps* pProps = &(It->first);
					CSoundBuffer* const pBuf = m_pSoundAssetManager->GetSoundBuffer(It->second);

					if (pBuf && (It->first.nPrecacheFlags & FLAG_SOUND_PRECACHE_LOAD_SOUND || It->first.nPrecacheFlags & FLAG_SOUND_PRECACHE_LOAD_GROUP ))
					{
						pBuf->Load(NULL);
					}

					if (gEnv->pSystem->IsDevMode() || gEnv->pCryPak->GetLvlResStatus())
					{
						// temporarily remove sound obstruction so playback is not delayed
						uint32 nFlags               = It->first.nFlags;
						uint32 const nFlagsExtended = It->first.nFlagsExtended;
						nFlags &= ~FLAG_SOUND_OBSTRUCTION;

						Precache(It->first.sName.c_str(), nFlags, It->first.nPrecacheFlags);
						_smart_ptr<ISound> pSound = LoadSound(It->first.sName.c_str(), nFlags, nFlagsExtended, It->first.nPrecacheFlags);

						if (pSound)
						{
							pSound->SetSemantic(eSoundSemantic_Sandbox);
							pSound->GetInterfaceExtended()->SetVolume(0.0f);
							pSound->Play();
							pSound->Stop(ESoundStopMode_AtOnce);
						}
						else
						{
							Log(eSLT_Warning, "<Sound> Create sound %s failed", It->first.sName.c_str());
							++nErrors;
						}
					}

				// Update It to the correct position right after the erased element
				// Do it this way since not all STLs support a direct assign during erase
				SoundBufferPropsDelayedMapItor ItTemp(It);
				++ItTemp;
				m_DelayedPrecaches.erase(It);
				It = ItTemp;

					// wrap
					if (It == ItEnd-1)
					{
						It = m_DelayedPrecaches.begin();
						m_nLastPrecacheIndex = 0;
					}

					ItEnd = m_DelayedPrecaches.end(); // renew ItEnd
					continue;
				}

				if (nErrors)
					Log(eSLT_Error, "%d Sound errors occurred, check log files!", nErrors);


				g_nLoadNonBlocking = nOldLoadNonBlocking;
			}
		}
	}
	else
	{
		fTimeLeft = g_fPrecacheDuration;
	}
}


bool CSoundSystem::GetRecordDeviceInfo(const int nRecordDevice, char* sName, int nNameLength)
{
	return false;
}

IMicrophone* CSoundSystem::CreateMicrophone(
																			const unsigned int nRecordDevice,
																			const unsigned int nBitsPerSample, 
																			const unsigned int nSamplesPerSecond,
																			const unsigned int nBufferSizeInSamples)
{
#if defined(SOUNDSYSTEM_USE_FMODEX400)
	IMicrophone * pMicrophone = new CMicrophone(m_pAudioDevice );
	m_Microphones.push_back(pMicrophone);
	return pMicrophone;
#else
	return NULL;
#endif
}

bool CSoundSystem::RemoveMicrophone( IMicrophone *pMicrophone )
{
	//remove Microphones
	MicroVecIter ItEnd = m_Microphones.end();
	for (MicroVecIter It=m_Microphones.begin(); It!=ItEnd; ++It)
	{	
		if (pMicrophone == (*It))
		{
			pMicrophone->Stop();
			m_Microphones.erase(It);
			return true;
		}
	}
	return false;
}

ISound* CSoundSystem::CreateNetworkSound(	INetworkSoundListener *pNetworkSoundListener,
																	 const unsigned int nBitsPerSample, 
																	 const unsigned int nSamplesPerSecond,
																	 const unsigned int nBufferSizeInSamples,
																	 EntityId PlayerID)
{
	//GUARD_HEAP;
	//FUNCTION_PROFILER( GetSystem(),PROFILE_SOUND );

	uint32 nFlags = /*FLAG_SOUND_DEFAULT_3D |*/ FLAG_SOUND_LOOP;

	IEntity *pWho = gEnv->pEntitySystem->GetEntity(PlayerID);
	
	if (!pWho)
		return NULL;

	SSoundBufferProps NetworkProp = SSoundBufferProps(pWho->GetName(), nFlags, 0, btNETWORK, 0, 0); 
	CSoundBuffer *pBufNetwork = m_pAudioDevice->CreateSoundBuffer(NetworkProp);

	//create a new instance	
	CSound* const pSound = (CSound* const)(GetNewSoundObject());

	if (!pSound || !pBufNetwork)
		return NULL; // something wrong

	pBufNetwork->GetInfo()->nBitsPerSample = nBitsPerSample;
	pBufNetwork->GetInfo()->nBaseFreq = nSamplesPerSecond;
	pBufNetwork->GetInfo()->nLengthInSamples = nBufferSizeInSamples;

	pSound->SetSoundBufferPtr(pBufNetwork);		
	pBufNetwork->LoadAsNetworkStream(pNetworkSoundListener);

	// Now create the platform sound
	IPlatformSound* pPlatformSound	= m_pAudioDevice->CreatePlatformSound(pSound, ""); // pWho->GetName()

	if (!pPlatformSound)
	{
		delete pPlatformSound;
		return NULL; // something wrong
	}

	pSound->SetPlatformSoundPtr(pPlatformSound);
	SetMinMaxRadius(*pSound, 0.1f, 25);

	AddToSoundSystem(pSound);
	
	return pSound;
}

void CSoundSystem::RemoveNetworkSound (ISound *pSound)
{
	pSound->Stop();
	pSound->UnloadBuffer(sbUNLOAD_ALL_DATA);

}

void CSoundSystem::RenderAudioInformation()
{
#ifdef INCLUDE_AUDIO_PRODUCTION_CODE
	IRenderer* const pRenderer = gEnv->pRenderer;

	if (pRenderer)
	{
		if (g_nSoundInfo)
		{
			int32 nEventCount = 0;
			int32 nGroupCount = 0;
			ptParamINT32 nTemp(nEventCount);
			m_pAudioDevice->GetParam(adpEVENTCOUNT, &nTemp);
			nTemp.GetValue(nEventCount);
			m_pAudioDevice->GetParam(adpGROUPCOUNT, &nTemp);
			nTemp.GetValue(nGroupCount);

			int nChannels = m_pAudioDevice->GetNumberSoundsPlaying();
			float fDSP = 0.0f;
			float fStream = 0.0f;
			float fGeometry = 0.0f;
			float fUpdate = 0.0f;
			float fTotal = 0.0f;
			bool bCPUUsage = m_pAudioDevice->GetCpuUsage(&fDSP, &fStream, &fGeometry, &fUpdate, &fTotal);
			bool bSpam = m_SpamFilter.WasThereSpam();
			bool bCacheStatic = false;
			//float fCacheSize = 0.0f;
			//float fCacheRSXSize = 0.0f;

			//ICVar* pCacheSize = gEnv->pConsole->GetCVar("s_CacheSize");
			//fCacheSize = pCacheSize ? pCacheSize->GetFVal() : 0.0f;

			ICVar* pCacheStatic = gEnv->pConsole->GetCVar("s_CacheStatic");
			bCacheStatic = (pCacheStatic && pCacheStatic->GetIVal());

			//ICVar* pCacheRSXSize = gEnv->pConsole->GetCVar("s_CacheRSXSize");
			//fCacheRSXSize = pCacheRSXSize ? pCacheRSXSize->GetFVal() : 0.0f;

			size_t const nBucketAllocatorPoolUsedSize		= g_MemoryPoolPrimary.GetSmallAllocsSize();
			size_t const nBucketAllocatorPoolAllocation	= g_MemoryPoolPrimary.GetSmallAllocsCount();
			size_t const nPrimaryPoolSize								= g_MemoryPoolPrimary.MemSize();
			size_t const nPrimaryPoolFreeSize						= g_MemoryPoolPrimary.MemFree();
			size_t const nPrimaryPoolUsedSize						= nPrimaryPoolSize - nPrimaryPoolFreeSize;
			size_t const nPrimaryPoolAllocation					= g_MemoryPoolPrimary.FragmentCount();
#ifdef PROVIDE_SECONDARY_POOL
			size_t const nSecondaryPoolSize							= g_MemoryPoolSoundSecondary.MemSize();
			size_t const nSecondaryPoolFreeSize					= g_MemoryPoolSoundSecondary.MemFree();
			size_t const nSecondaryAllocation						= g_MemoryPoolSoundSecondary.FragmentCount();
#else
			size_t const nSecondaryPoolSize							= 0;
			size_t const nSecondaryPoolFreeSize					= 0;
			size_t const nSecondaryAllocation						= 0;
#endif
			size_t const nSecondaryPoolUsedSize					= nSecondaryPoolSize - nSecondaryPoolFreeSize;

			size_t const nSoundNameCRCSize			= m_SoundNameCacheCRC.size();

			//uint32 nMemoryPoolSystemSize = m_MemoryPoolSystem.AllocatedMemory();

			//unsigned int nSoundMemAll = 0;
			//unsigned int nSoundMemMain = 0;
			//unsigned int nSoundMemRSX = 0;
			//unsigned int nSystemMem = 0;
			//unsigned int nSoundCacheFillMain = 0;
			//unsigned int nSoundCacheFillSecondary = 0;
			//unsigned int nSoundCacheSizeMain = 0;
			//unsigned int nSoundCacheSizeSecondary = 0;
			//m_pAudioDevice->GetMemoryStats(admqSOUNDSECONDARY, &nSoundMemRSX);
			//m_pAudioDevice->GetMemoryStats(admqSOUNDALL, &nSoundMemAll);
			//m_pAudioDevice->GetMemoryStats(admqALL, &nSystemMem);
			//m_pAudioDevice->GetMemoryStats(admqCACHEFILLMAIN, &nSoundCacheFillMain);
			//m_pAudioDevice->GetMemoryStats(admqCACHEFILLSECONDARY, &nSoundCacheFillSecondary);
			//m_pAudioDevice->GetMemoryStats(admqCACHESIZEMAIN, &nSoundCacheSizeMain);
			//m_pAudioDevice->GetMemoryStats(admqCACHESIZESECONDARY, &nSoundCacheSizeSecondary);

			//if (nSoundMemRSX)
			//m_pAudioDevice->GetMemoryStats(admqSOUNDMAIN, &nSoundMemMain);

			int nPlayingSounds = 0;
			int nUsedSoundIDs = 0;
			int nEntitySounds = 0;
			std::map<string, std::vector<string> > SoundEntity;
			EntityId SoundProxyEntityID = 0;


			TSoundIDSet::const_iterator ItEnd = m_currentSoundIDs.end();
			for (TSoundIDSet::const_iterator It=m_currentSoundIDs.begin(); It!=ItEnd; ++It)
			{
				CSound* const pSound = (CSound* const)(GetSound(*It));

				if (pSound)
				{
					++nUsedSoundIDs;
					SoundProxyEntityID = pSound->GetSoundProxyEntityID();

					if (SoundProxyEntityID != 0)
					{
						IEntity *pEntity = gEnv->pEntitySystem->GetEntity(SoundProxyEntityID);
						if (pEntity)
						{
							std::map<string, std::vector<string> >::iterator SPIt = SoundEntity.find(CONST_TEMP_STRING(pEntity->GetName()));

							if (SPIt == SoundEntity.end())
							{
								// insert new
								SoundEntity[pEntity->GetName()].push_back(pSound->GetName());
							}
							else
							{
								SPIt->second.push_back(pSound->GetName());

							}
							++nEntitySounds;
						}
					}

					if (pSound->IsPlayingOnChannel())
						++nPlayingSounds;
				}
			}

			// renders default stuff in the top left corner
			float x = 20.0f;
			float fColor[4]				={1.0f, 1.0f, 1.0f, 0.7f};
			float fColorRed[4]		={1.0f, 0.0f, 0.0f, 0.7f};
			float fColorYellow[4]	={1.0f, 1.0f, 0.0f, 0.7f};
			float fColorGreen[4]	={0.0f, 1.0f, 0.0f, 0.7f};
			float fColorOrange[4]	={1.0f, 0.5f, 0.0f, 0.7f};
			float fColorBlue[4]		={0.4f, 0.4f, 7.0f, 0.7f};

			float ypos = 2.0f;

			m_pAudioDevice->DrawInformation(pRenderer, x, ypos, g_nSoundInfo);
			pRenderer->Draw2dLabel(x + 400.0f, ypos, 1.35f, fColorRed, false, "%s", bSpam ? "<CHECK CONSOLE FOR ERROR MESSAGES>" : "");
			ypos += 11.0f;

			// Determine budget situation and indicate appropriate
			float const fPoolPrimaryRatio						= nPrimaryPoolSize ? nPrimaryPoolUsedSize/(float)nPrimaryPoolSize : 0.0f;
			bool const bPrimaryPoolOutOfBudget			=	fPoolPrimaryRatio > g_fMemoryPoolSoundPrimaryRatio;
			pRenderer->Draw2dLabel(x, ypos , 1.35f, fColor, false, "Bucket: %d / ? KiB (<%d> Ratio ?)", (int)g_MemoryPoolPrimary.GetSmallAllocsSize() / 1024, (int)g_MemoryPoolPrimary.GetSmallAllocsCount());
			pRenderer->Draw2dLabel(bPrimaryPoolOutOfBudget ? x + 300.0f : x + 300.0f, bPrimaryPoolOutOfBudget ? 460.0f : ypos, bPrimaryPoolOutOfBudget ? 3.0f : 1.35f, bPrimaryPoolOutOfBudget ? fColorRed : fColor, false, "Primary: %.2f / %.2f MB (<%d> Ratio %.2f)", ((nPrimaryPoolSize-nPrimaryPoolFreeSize)/1024)/1024.0f, (nPrimaryPoolSize/1024)/1024.0f, (int)nPrimaryPoolAllocation, g_fMemoryPoolSoundPrimaryRatio);
#ifdef PROVIDE_SECONDARY_POOL
			float const fPoolSecondaryRatio					= nSecondaryPoolSize ? nSecondaryPoolUsedSize/(float)nSecondaryPoolSize : 0.0f;
			bool const bSecondaryPoolOutOfBudget		=	fPoolSecondaryRatio	> g_fMemoryPoolSoundSecondaryRatio;
			pRenderer->Draw2dLabel(bSecondaryPoolOutOfBudget ? x + 300.0f : x + 600.0f, bSecondaryPoolOutOfBudget ? 500.0f : ypos, bSecondaryPoolOutOfBudget ? 3.0f : 1.35f, bSecondaryPoolOutOfBudget ? fColorRed : fColor, false, "Secondary: %.2f / %.2f MB (<%d> Ratio %.2f)",((nSecondaryPoolSize-nSecondaryPoolFreeSize)/1024)/1024.0f, (nSecondaryPoolSize/1024)/1024.0f, (int)nSecondaryAllocation, g_fMemoryPoolSoundSecondaryRatio);
#endif
			//pRenderer->Draw2dLabel(bLargestFreeBlockOutOfBudget ? x + 300.0f : x + 900.0f, bLargestFreeBlockOutOfBudget ? 540.0f : ypos, bLargestFreeBlockOutOfBudget ? 3.0f : 1.35f, bLargestFreeBlockOutOfBudget ? fColorRed : fColor, false, "Free Block: %d", nPoolPrimaryFreeBlock);
			ypos += 11.0f;
			pRenderer->Draw2dLabel(x, ypos, 1.35f, fColor, false, "  Current Events: %d (Channels: %d), DSP: %3.1f, Stream: %3.1f, Geometry: %3.1f, CPU: %3.1f, Total: %3.1f, Names-CRC %d", GetUsedVoices(), nChannels, fDSP, fStream, fGeometry, fUpdate, fTotal, (int)nSoundNameCRCSize);
			ypos += 11.0f;
			//pRenderer->Draw2dLabel(x, 20, 1.35f, fColor, false, "  Loaded SoundBuffers: %d (%d)", m_nBuffersLoaded, m_soundBuffers.size());
			pRenderer->Draw2dLabel(x, ypos, 1.35f, fColor, false, "  SoundBuffers %d\tTrashedCount %d", (int)m_pSoundAssetManager->GetNumberBuffersLoaded(), (int)CSoundSystem::g_nBuffersTrashed);

			// Now render the load requests per non-blocking loading thread
			pRenderer->Draw2dLabel(x + 280.0f, ypos, 1.35f, fColor, false, "Load Requests:");
			
			// Table for thread offsets
			float const afThreadOffsets[FMOD_MAX_NUM_NONBLOCKING_LOADING_THREADS] =
			{
				400.0f,
				400.0f + 100.0f,
				400.0f + 2.0f * 100.0f,
				400.0f + 3.0f * 100.0f,
				400.0f + 4.0f * 100.0f
			};

			// Draw the header
			stack_string sThread;

			for (size_t i = 0; i < (size_t)g_SoundCVars.g_nNumLoadingThreadsToUse; ++i)
			{
				sThread.Format("Thread #%d: %d", i+1, m_pAudioDevice->GetNumLoadRequests(i));
				pRenderer->Draw2dLabel(afThreadOffsets[i], ypos, 1.35f, fColor, false, "%s", sThread.c_str());
			}

			TLoadRequestInfosVec::reverse_iterator IterLoadRequests(m_aLoadRequestInfos.rbegin());
			TLoadRequestInfosVec::reverse_iterator const IterLoadRequestsEnd(m_aLoadRequestInfos.rend());			

			float fOffsetY = 0.0f;

			for (; IterLoadRequests != IterLoadRequestsEnd; ++IterLoadRequests)
			{
				SLoadRequestInfo const& oLoadRequestInfo(*IterLoadRequests);
				CTimeValue const oTimeDiff(gEnv->pTimer->GetFrameStartTime() - oLoadRequestInfo.oTimeCreated);
				float const fTimeDiffInMS = oTimeDiff.GetMilliSeconds();

				// Throw them out after a certain amount of time
				if (fTimeDiffInMS < 0.0f || fTimeDiffInMS > g_SoundCVars.g_fLoadRequestsDisplayTime)
				{
					// Necessary as the compiler complains about "error C2105: '--' needs l-value".
					TLoadRequestInfosVec::iterator ItDel(IterLoadRequests.base());
					m_aLoadRequestInfos.erase(--ItDel);
					break; // Don't care about removing several in one run
				}

				float fEntryColor[4] = {0.0f, 0.0f, 0.0f, 0.0f};

				switch (oLoadRequestInfo.nThreadID)
				{
				case 0: // FMOD_EVENT_NONBLOCKING_THREAD0
					{
						memcpy(fEntryColor, fColorRed, sizeof(fEntryColor));
						break;
					}
				case 1: // FMOD_EVENT_NONBLOCKING_THREAD1
					{
						memcpy(fEntryColor, fColorYellow, sizeof(fEntryColor));
						break;
					}
				case 2: // FMOD_EVENT_NONBLOCKING_THREAD2
					{
						memcpy(fEntryColor, fColorGreen, sizeof(fEntryColor));
						break;
					}
				case 3: // FMOD_EVENT_NONBLOCKING_THREAD3
					{
						memcpy(fEntryColor, fColorOrange, sizeof(fEntryColor));
						break;
					}
				case 4: // FMOD_EVENT_NONBLOCKING_THREAD4
					{
						memcpy(fEntryColor, fColorBlue, sizeof(fEntryColor));
						break;
					}
				}

				fEntryColor[3] = max(1.0f - (fTimeDiffInMS / g_SoundCVars.g_fLoadRequestsDisplayTime), 0.3f);
				fOffsetY       += 11.0f;
				pRenderer->Draw2dLabel(afThreadOffsets[oLoadRequestInfo.nThreadID] , ypos + fOffsetY, 1.35f, fEntryColor, false, "%s (%s)", oLoadRequestInfo.sSoundBankName.c_str(), oLoadRequestInfo.sEventName.c_str());
			}

			ypos += 11.0f;
			pRenderer->Draw2dLabel(x, ypos, 1.35f, fColor, false, "  Groups Total: %d, Events Total: %d", nGroupCount, nEventCount);
			ypos += 11.0f;
			pRenderer->Draw2dLabel(x, ypos, 1.35f, fColor, false, "  Sounds Active: %d, Inactive: %d, Total: %d", (int)m_OneShotSounds.size()+(int)m_LoopedSounds.size(), (int)m_InactiveSounds.size(), nUsedSoundIDs);
			ypos += 11.0f;

			//pRenderer->Draw2dLabel(x, 170, 1.5, fColor, false, "  Total size of vecSounds: %d, ", m_AllSoundsArray.size());		

			tVecListenersIterConst const LIterEnd(m_vecListeners.end());
			tVecListenersIterConst LIter(m_vecListeners.begin());

			for (; LIter != LIterEnd; ++LIter)
			{
				CListener const* const pListener = (*LIter);
				bool const bActive               = pListener->GetActive();
				float const fRecord              = pListener->GetRecordLevel();
				ListenerID const nListenerID     = pListener->GetID();
				Vec3 vTemp(pListener->GetPosition());

				float const fColorListener[4] = {bActive?fColorGreen[0]:fColorRed[0],
					                               bActive?fColorGreen[1]:fColorRed[1],
																				 bActive?fColorGreen[2]:fColorRed[2],
																				 clamp(fRecord+0.3f, 0.0f, 1.0f)};

				if (pListener->bInside && pListener->GetVisArea())
				{
					if (g_nSoundInfo != 15)
					{
						pRenderer->Draw2dLabel(x, ypos, 1.35f, fColorListener, false, "  Listener %d INSIDE: Vis area=: %s PosXYZ: %.2f %.2f %.2f", nListenerID, pListener->GetVisArea()->GetName(), vTemp.x, vTemp.y, vTemp.z);
					}
					else
					{
						pRenderer->Draw2dLabel(x, ypos, 1.35f, fColorListener, false, "  Listener %d INSIDE: Vis area=: %s", nListenerID, pListener->GetVisArea()->GetName());
					}
				}
				else
				{
					if (g_nSoundInfo != 15)
					{
						pRenderer->Draw2dLabel(x, ypos, 1.35f, fColorListener, false, "  Listener %d OUTSIDE PosXYZ: %.2f %.2f %.2f", nListenerID, vTemp.x, vTemp.y, vTemp.z);
					}
					else
					{
						pRenderer->Draw2dLabel(x, ypos, 1.35f, fColorListener, false, "  Listener %d OUTSIDE", nListenerID);
					}
				}

				if (g_nSoundInfo == 15)
				{
					ypos += 11.0f;
					pRenderer->Draw2dLabel(x, ypos, 1.35f, fColorListener, false, "  PosXYZ: %.2f %.2f %.2f", vTemp.x, vTemp.y, vTemp.z);
					ypos += 11.0f;
					vTemp = pListener->GetForward();
					pRenderer->Draw2dLabel(x, ypos, 1.35f, fColorListener, false, "  ForXYZ: %.2f %.2f %.2f", vTemp.x, vTemp.y, vTemp.z);
					ypos += 11.0f;
					vTemp = pListener->GetTop();
					pRenderer->Draw2dLabel(x, ypos, 1.35f, fColorListener, false, "  TopXYZ: %.2f %.2f %.2f", vTemp.x, vTemp.y, vTemp.z);
					ypos += 11.0f;
					vTemp = pListener->GetVelocity();
					pRenderer->Draw2dLabel(x, ypos, 1.35f, fColorListener, false, "  VelXYZ: %.2f %.2f %.2f", vTemp.x, vTemp.y, vTemp.z);
				}

				ypos += 11.0f;
			}

			// Determining current overall streams count and displaying a warning once exceeding
			int nOverallStreamsCount = 0;

#if defined(SOUNDSYSTEM_USE_FMODEX400)
			FMOD::EventSystem* const pEventSystem = (FMOD::EventSystem*)m_pAudioDevice->GetEventSystem();

			if (pEventSystem)
			{
				int nNum = 64;
				char* sName = NULL;
				FMOD_EVENT_SYSTEMINFO SystemInfo;
				memset(&SystemInfo, 0, sizeof(FMOD_EVENT_SYSTEMINFO));

				SystemInfo.numplayingevents = nNum;
				FMOD_EVENT* PlayingEvents[64];
				SystemInfo.playingevents = PlayingEvents;

				FMOD_RESULT oResult = pEventSystem->getInfo( &SystemInfo );

				if (oResult == FMOD_OK)
				{
					for (int i=0; i<SystemInfo.numplayingevents; ++i)
					{
						FMOD::Event* pEvent = (FMOD::Event*)SystemInfo.playingevents[i];
						FMOD_EVENT_INFO EventInfo;
						FMOD_EVENT_WAVEBANKINFO WavebankInfo = {{0},0};
						EventInfo.maxwavebanks = 1;
						EventInfo.wavebankinfo = &WavebankInfo;

						oResult = pEvent->getInfo(NULL, &sName, &EventInfo);

						if (oResult != FMOD_OK)
						{
							Log(eSLT_Warning, "<Sound> event (%s) get info failed! (SoundInfo)\n", sName);
						}
						else if (WavebankInfo.type == 0)
						{
							++nOverallStreamsCount;
						}
					}
				}
				else
					Log(eSLT_Warning, "<Sound> event system get system info failed! (SoundInfo)\n");
			}
#endif 

			int nMaxStreams = 0;

#if defined(WIN32) || defined(WIN64)
			nMaxStreams = 5;






#endif

			if (nOverallStreamsCount < 0 || nOverallStreamsCount >= nMaxStreams)
			{
				pRenderer->Draw2dLabel(100.0f, 600.0f, 3.0f, fColorRed, false, "Too many concurrent audio streams!");
				pRenderer->Draw2dLabel(100.0f, 630.0f, 3.0f, fColorRed, false, "Max: %d Current: %d", nMaxStreams, nOverallStreamsCount);
			}

			// soundinfo 1 shows only playing sound in the middle
			// soundinfo 2 adds inactive and active looping sounds to the left
			// soundinfo 3 adds oneshots sounds to the left
			// soundinfo 4 shows only "lost" sounds on the left
			// soundinfo 5 shows loaded event soundbuffers on the left
			// soundinfo 6 shows loaded soundbuffers on the left (dialog)
			// soundinfo 7 shows loaded wavebanks on the left
			// soundinfo 8 shows music debug
			// soundinfo 9 shows Sound Moods
			// soundinfo 10 shows Sound Moods values set within FMOD
			// soundinfo 11 Shows memory usage by project
			// soundinfo 12 Shows memory usage by project/group
			// soundinfo 13 Shows memory usage by project/group:sound
			// soundinfo 14 shows overall memory stats

			switch(g_nSoundInfo)
			{
			case 1 :
				{
					ypos += 5;
					if (m_pReverbManager)
						m_pReverbManager->DrawInformation(pRenderer, x, ypos);

					ypos	= 120;
					pRenderer->Draw2dLabel(x, ypos, 1, fColor, false, "----- All Playing Sounds: %d -----", nPlayingSounds );
					ypos += 10;

					break;
				}
			case 2:
				{
					ypos	= 120;
					pRenderer->Draw2dLabel(x, ypos, 1, fColor, false, "----- Detailed Looping Sounds: %d -----", nPlayingSounds );
					ypos += 10;
					break;
				}
			case 3:
				{
					ypos	= 120;
					pRenderer->Draw2dLabel(x, ypos, 1, fColor, false, "----- Detailed OneShot Sounds: %d -----", nPlayingSounds );
					ypos += 10;
					break;
				}
			case 4:
				{
					ypos	= 120;
					pRenderer->Draw2dLabel(x, ypos, 1, fColor, false, "----- Lost Sounds: %d -----", nPlayingSounds );
					pRenderer->Draw2dLabel(x+600, ypos, 1, fColor, false, "----- Entity Sounds: %d -----", nEntitySounds );
					ypos += 10;
					break;
				}
			case 5 :
				ypos = 100;
				m_pSoundAssetManager->DrawInformation(pRenderer, x, ypos, amdiONLYEVENTBUFFERS);
				break;
			case 6 :
				ypos = 100;
				m_pSoundAssetManager->DrawInformation(pRenderer, x, ypos, amdiONLYSAMPLEBUFFERS);
				break;
			case 7 :
				{
					ypos = 100.0f;
					pRenderer->Draw2dLabel(x, ypos, 1.0f, fColor, false, "----- Wavebanks ----- (set s_Profiling 1)" );

					float fPosY = ypos;
					pRenderer->Draw2dLabel(600.0f, fPosY, 1.5f, fColor, false, "Delayed Precaches: %d", (int)m_DelayedPrecaches.size());
					fPosY += 15.0f;

					SoundBufferPropsDelayedMapItorConst ItEndDelayed = m_DelayedPrecaches.end();
					for (SoundBufferPropsDelayedMapItorConst It = m_DelayedPrecaches.begin(); It != ItEndDelayed; ++It)
					{
						pRenderer->Draw2dLabel(600.0f, fPosY, 1.3f, fColor, false, "%s", (*It).first.sName.c_str());
						fPosY += 13.0f;
					}

					break;
				}
			case 8 :
				{
					ypos = 80;

					if (gEnv->pGame)
					{
						if (gEnv->pGame->GetIGameFramework())
						{
							if (gEnv->pGame->GetIGameFramework()->GetMusicLogic())
							{
								gEnv->pGame->GetIGameFramework()->GetMusicLogic()->DrawInformation(pRenderer, x, ypos, g_nSoundInfo);
							}
						}
					}

					gEnv->pMusicSystem->DrawInformation(pRenderer, x, ypos);

					break;
				}
			case 9 :
			case 10 :
				if (m_pMoodManager)
					m_pMoodManager->DrawInformation(pRenderer, x, ypos, g_nSoundInfo);
				break;

			case 14:
				{
					ypos += 15.0f;
					pRenderer->Draw2dLabel(x, ypos += 15.0f, 1.8f, fColor, false, "Bucket: %d / ? KiB (<%d> Ratio ?)", (int)(g_MemoryPoolPrimary.GetSmallAllocsSize() / 1024), (int)g_MemoryPoolPrimary.GetSmallAllocsCount());
					pRenderer->Draw2dLabel(x, ypos += 15.0f, 1.8f, fColor, false, "Primary: %.2f / %.2f MB (<%d> Ratio %.2f)", ((nPrimaryPoolSize-nPrimaryPoolFreeSize)/1024)/1024.0f, (nPrimaryPoolSize/1024)/1024.0f, (int)nPrimaryPoolAllocation, g_fMemoryPoolSoundPrimaryRatio);

#ifdef PROVIDE_SECONDARY_POOL
					pRenderer->Draw2dLabel(x, ypos += 15.0f, 1.8f, fColor, false, "Secondary: %.2f / %.2f MB (<%d> Ratio %.2f)",((nSecondaryPoolSize-nSecondaryPoolFreeSize)/1024)/1024.0f, (nSecondaryPoolSize/1024)/1024.0f, (int)nSecondaryAllocation, g_fMemoryPoolSoundSecondaryRatio);
#endif // PROVIDE_SECONDARY_POOL

					ypos += 15.0f;
					pRenderer->Draw2dLabel(x, ypos += 15.0f, 1.8f, fColor, false, "Normal Count %d", (int)g_oSoundMemoryInfo.nMemTypeNormalCount);
					pRenderer->Draw2dLabel(x, ypos += 15.0f, 1.8f, fColor, false, "Stream File Count %d", (int)g_oSoundMemoryInfo.nMemTypeStreamFileCount);
					pRenderer->Draw2dLabel(x, ypos += 15.0f, 1.8f, fColor, false, "Stream Decode Count %d", (int)g_oSoundMemoryInfo.nMemTypeStreamDecodeCount);
					pRenderer->Draw2dLabel(x, ypos += 15.0f, 1.8f, fColor, false, "X360 Physical Count %d", (int)g_oSoundMemoryInfo.nMemTypeXBox360PhysicalCount);
					pRenderer->Draw2dLabel(x, ypos += 15.0f, 1.8f, fColor, false, "Persistent Count %d", (int)g_oSoundMemoryInfo.nMemTypePersistentCount);
					pRenderer->Draw2dLabel(x, ypos += 15.0f, 1.8f, fColor, false, "Secondary Count %d", (int)g_oSoundMemoryInfo.nMemTypeSecondaryCount);
					ypos += 15.0f;

					for (size_t i = 0; i < SSoundMemoryInfo::eAllocationsArraySize; ++i)
					{
						pRenderer->Draw2dLabel(x, ypos += 11.0f, 1.35f, fColor, false, "Alloc %d - %d", 1 << i , (int)g_oSoundMemoryInfo.anAllocations[i]);
					}
					break;
				}

			default :
				break;
			} //switch

			float oldypos = ypos;

			// per sound soundinfo
			TSoundIDSet::const_iterator IterEnd = m_currentSoundIDs.end();
			for (TSoundIDSet::const_iterator It=m_currentSoundIDs.begin(); It!=IterEnd; ++It)
			{
				CSound* const pSound = (CSound* const)(GetSound(*It));

				if (pSound)
				{
					SoundProxyEntityID = pSound->GetSoundProxyEntityID();

					bool bIsStream = (pSound->GetFlags() & FLAG_SOUND_STREAM) != 0;

					// "lost" color is default
					float *pCurColor = fColorRed;
					bool bFound = false;

					if (m_InactiveSounds.Find(pSound))
					{
						pCurColor = fColor;
						bFound = true;
					}

					if (m_LoopedSounds.Find(pSound))
					{
						pCurColor = fColorYellow;
						bFound = true;
					}

					if (m_OneShotSounds.Find(pSound) && g_nSoundInfo > 2)
					{
						pCurColor = fColorGreen;
						bFound = true;
					}

					// static sounds in a soundproxy are orange
					if (pCurColor == fColorRed && !bFound)
					{
						// if the sound is playing anyway then its a static sound used by a sound proxy
						if (SoundProxyEntityID != 0)
						{
							pCurColor = fColorBlue;

							IEntity *pEntity = gEnv->pEntitySystem->GetEntity(SoundProxyEntityID);
							if (pEntity)
							{
								IEntitySoundProxy *pSoundProxy = (IEntitySoundProxy*)pEntity->GetProxy(ENTITY_PROXY_SOUND);
								if (pSoundProxy)
								{
									if (pSoundProxy->GetStaticSound(pSound->GetId()))
										pCurColor = fColorOrange;
								}
							}
							bFound = true;
						}
					}

					switch(g_nSoundInfo)
					{
					case 1 :
						{
							_smart_ptr<IPlatformSound> ps = pSound->GetPlatformSoundPtr();

							if (ps && !ps->GetSoundHandle())
								continue;

							if (!pSound->IsPlayingOnChannel())
								continue;

							pCurColor = fColor;

							if (pSound->GetFlags() & FLAG_SOUND_3D)
							{
								Vec3 SoundPosition = pSound->GetPosition();

								pRenderer->Draw2dLabel(x, ypos, 1.2f, bIsStream?fColorOrange:pCurColor, false, "3D %s",pSound->GetName());
							}
							else
							{
								pRenderer->Draw2dLabel(x, ypos, 1.2f, bIsStream?fColorOrange:pCurColor, false, "%s",pSound->GetName());
							}

							ypos += 10;
							break;
						} // 1
					case 2: case 3:
						{
							if (bFound && pCurColor != fColorRed && ( (g_nSoundInfo == 2 && pSound->GetLoopMode() ) || (g_nSoundInfo == 3 && !pSound->GetLoopMode() ) ) )
							{
								uint32 nSoundID = pSound->GetId();
								if (pSound->IsPlayingOnChannel())
									pRenderer->Draw2dLabel(5, ypos, 1.1f, fColorGreen, false, "[%d]", nSoundID);
								else //if (cs->IsActive())
									pRenderer->Draw2dLabel(5, ypos, 1.1f, fColor, false, "[%d]", nSoundID);

								float fSpread = 0.0f;
								pSound->GetParam("spread", &fSpread, false);
								Vec3 SoundPosition = pSound->GetPosition();

								char sText[512];
								size_t nTextLen = 0;

								sprintf(sText,"%s, %s",pSound->GetName(), (pSound->GetFlags()&FLAG_SOUND_3D)?"3D":"2D");

								if (true)
								{
									nTextLen = strlen(sText);
									sprintf(sText+nTextLen,", pos=%d, %d, %d",(int)(SoundPosition.x),(int)(SoundPosition.y),(int)(SoundPosition.z));
								}

								if (true)
								{
									nTextLen = strlen(sText);
									sprintf(sText+nTextLen,", vol=%1.2f", pSound->GetActiveVolume());
								}

								if (pSound->GetFlags() & FLAG_SOUND_RADIUS)
								{
									nTextLen = strlen(sText);
									sprintf(sText+nTextLen,", minR=%d, maxR=%d",(int)(pSound->GetMinRadius()),(int)(pSound->GetMaxDistance()));
								}

								if (pSound->GetFlags() & FLAG_SOUND_PARAM_SPREAD)
								{
									nTextLen = strlen(sText);
									sprintf(sText+nTextLen,", spread=%1.2f", fSpread);
								}

								if ((pSound->GetFlags() & FLAG_SOUND_OBSTRUCTION) && (pSound->GetFlags() & FLAG_SOUND_3D) && pSound->GetObstruction())
								{
									nTextLen = strlen(sText);
									sprintf(sText+nTextLen,", obstr=%1.2f", pSound->GetObstruction()->GetAccumulatedDirect());
								}

								// static sounds
								bool bIsStatic = false;
								if (GetISystem() && gEnv->pEntitySystem)
								{
									IEntity *pEntity = gEnv->pEntitySystem->GetEntity(SoundProxyEntityID);
									if (pEntity)
									{
										IEntitySoundProxy *pSoundProxy = (IEntitySoundProxy*)pEntity->GetProxy(ENTITY_PROXY_SOUND);
										if (pSoundProxy)
										{
											if (pSoundProxy->GetStaticSound(pSound->GetId()))
											{
												bIsStatic = true;
											}
										}
									}
								}

								if (SoundProxyEntityID)
								{
									size_t idLen = strlen(sText);
									sprintf(sText+idLen,", proxy=%d", SoundProxyEntityID);

									if (bIsStatic)
									{
										idLen = strlen(sText);
										sprintf(sText+idLen,", static=%d", bIsStatic);
										pCurColor = fColorOrange;

									}
								}

								if (bFound)	
								{
									pRenderer->Draw2dLabel(x+40, ypos, 1.1f, bIsStream?fColorOrange:pCurColor, false, "%s", sText);
									ypos += 10;
								}
							}
							break;
						} // 2
					case 4:
						{
							if (!bFound)
							{
								uint32 nSoundID = pSound->GetId();
								if (pSound->IsPlayingOnChannel())
									pRenderer->Draw2dLabel(5, ypos, 1, fColorGreen, false, "[%d]", nSoundID);
								else //if (cs->IsActive())
									pRenderer->Draw2dLabel(5, ypos, 1, fColor, false, "[%d]", nSoundID);


								if (pSound->GetFlags() & FLAG_SOUND_3D)
								{
									Vec3 SoundPosition = pSound->GetPosition();

									if (pSound->GetFlags() & FLAG_SOUND_VOICE)
										pRenderer->Draw2dLabel(x+40, ypos, 1, pCurColor, false, "Dialog: %s,3d, pos=%d, %d, %d, minR=%d, maxR=%d, vol=%1.2f", pSound->GetName(),(int)(SoundPosition.x),(int)(SoundPosition.y),(int)(SoundPosition.z),(int)(pSound->GetMinRadius()),(int)(pSound->GetMaxDistance()),pSound->GetActiveVolume() );
									else
										pRenderer->Draw2dLabel(x+40, ypos, 1, pCurColor, false, "%s,3d, pos=%d, %d, %d, minR=%d, maxR=%d, vol=%1.2f", pSound->GetName(),(int)(SoundPosition.x),(int)(SoundPosition.y),(int)(SoundPosition.z),(int)(pSound->GetMinRadius()),(int)(pSound->GetMaxDistance()),pSound->GetActiveVolume() );
								}
								else
								{
									if (pSound->GetFlags() & FLAG_SOUND_VOICE)
										pRenderer->Draw2dLabel(x+40, ypos, 1, pCurColor, false, "Dialog: %s,2d, volume=%1.2f", pSound->GetName(), pSound->GetActiveVolume() );
									else
										pRenderer->Draw2dLabel(x+40, ypos, 1, pCurColor, false, "%s,2d, volume=%1.2f", pSound->GetName(), pSound->GetActiveVolume() );
								}

								ypos += 10;
							}
							break;
						} // 4
					} // switch
				} // pSound
			} //it

			// Draw SoundEntites
			if (g_nSoundInfo == 4)
			{
				// Entity Sounds
				std::map<string, std::vector<string> >::const_iterator const SPItEnd(SoundEntity.end());
				std::map<string, std::vector<string> >::const_iterator SPIt(SoundEntity.begin());

				for (; SPIt != SPItEnd; ++SPIt)
				{
					float const* pfCurColor = fColor;

					if (SPIt->second.size() > 5)
					{
						pfCurColor = fColorOrange;
					}

					if (SPIt->second.size() > 10)
					{
						pfCurColor = fColorRed;
					}

					pRenderer->Draw2dLabel(x+600, oldypos, 1, pfCurColor, false, "%s [%d]", SPIt->first.c_str(), SPIt->second.size());

					std::vector<string>::const_iterator const ItNamesEnd(SPIt->second.end());
					std::vector<string>::const_iterator ItNames(SPIt->second.begin());

					for (;ItNames != ItNamesEnd; ++ItNames)
					{
						oldypos += 10.0f;
						pRenderer->Draw2dLabel(x+620, oldypos, 1, pfCurColor, false, "%s", (*ItNames).c_str());
					}

					oldypos += 10;
				}
			}
		}

		// Drawing a red ball at the sound position and write the filename next to it
		if (g_nDrawSounds)
		{
			IRenderAuxGeom* pAuxGeom( pRenderer->GetIRenderAuxGeom() );
			pAuxGeom->SetRenderFlags( e_Def3DPublicRenderflags );
			float colorwhite[4]={1,1,1,1};

			// draw a green ball where a listener is
			tVecListenersIterConst LIterEnd = m_vecListeners.end();
			for (tVecListenersIterConst LIter = m_vecListeners.begin(); LIter != LIterEnd; ++LIter)
			{
				if ((*LIter)->bActive)
					pAuxGeom->DrawSphere( (*LIter)->GetPosition(), 0.2f, ColorB( 10, 255, 10, 255 ) );	
				else
					pAuxGeom->DrawSphere( (*LIter)->GetPosition(), 0.2f, ColorB( 10, 10, 255, 255 ) );	
				pRenderer->DrawLabelEx((*LIter)->GetPosition(), 1.3f, colorwhite, true, false, "Listener: %d RecordLevel:%1.2f", (*LIter)->nListenerID, (*LIter)->fRecordLevel);
			}

			TSoundIDSet::const_iterator ItEnd = m_currentSoundIDs.end();
			for (TSoundIDSet::const_iterator It=m_currentSoundIDs.begin(); It!=ItEnd; ++It)
			{
				CSound* const pSound = (CSound* const)(GetSound(*It));

				if (pSound)
				{
					_smart_ptr<IPlatformSound> pPlatformSound = pSound->GetPlatformSoundPtr();

					float fSoundAmplitude = 0.0f;
					int nSoundHandle = 0;
					if (pPlatformSound)
					{
						ptParamF32 TempParam(fSoundAmplitude);
						pPlatformSound->GetParamByType(pspAMPLITUDE, &TempParam);
						TempParam.GetValue(fSoundAmplitude);
						nSoundHandle = (int)(INT_PTR)pPlatformSound->GetSoundHandle();
					}


					Vec3 vPos = pSound->GetPlaybackPosition();
					Vec3 vListenerPos;

					// Joes request to not draw sounds right in front of the face
					IListener const* const pListener = GetListener(GetClosestActiveListener(vPos));
					
					if (pListener)
					{
						vListenerPos = pListener->GetPosition();
						
						if (vPos.GetSquaredDistance(vListenerPos) > 1.5f)
						{
							bool bDrawSound = false;

							switch (g_nDrawSounds)
							{
							case 1:
								{
									if (pSound->IsPlayingOnChannel())
									{
										bDrawSound = true;
										pRenderer->DrawLabelEx(vPos,1.3f,colorwhite, true, false, "%s Vol. on Channel:%1.2f", pSound->GetName(), pSound->GetActiveVolume());
									}

									break;
								}
							case 2:
								{
									if (pSound->IsPlayingOnChannel())
									{
										bDrawSound = true;
										pRenderer->DrawLabelEx(vPos,1.3f,colorwhite, true, false, "Channel: %d static Vol.:%1.2f, Vol. on Channel:%1.2f, Sound-ID: %d",nSoundHandle, pSound->GetActiveVolume(), fSoundAmplitude, pSound->GetId());
									}

									break;
								}
							case 3:
								{
									if (pSound->IsPlayingOnChannel())
									{
										bDrawSound = true;
										float fDirect = 0.0f;
										float fReverb = 0.0f;
										if (pSound->GetObstruction())
										{
											fDirect = pSound->GetObstruction()->GetAccumulatedDirect();
											fReverb = pSound->GetObstruction()->GetAccumulatedReverb();
										}

										pRenderer->DrawLabelEx(vPos,1.3f,colorwhite, true, false, "%s\n static Vol.:%1.2f, Vol. on Channel:%1.2f\n Channel: %d ID: %d\n DirectOcc.: %f ReverbOcc.: %f",pSound->GetName(),pSound->GetActiveVolume(), fSoundAmplitude, nSoundHandle, pSound->GetId(), fDirect, fReverb);
									}

									break;
								}
							case 4:
								{
									bDrawSound = true;
									pRenderer->DrawLabelEx(vPos, 1.3f, colorwhite, true, false, "%s\n static Vol.:%1.2f, Vol. on Channel:%1.2f, \n Channel: %d Sound-ID: %d",pSound->GetName(),pSound->GetActiveVolume(), fSoundAmplitude, nSoundHandle, pSound->GetId());

									if (((pSound->GetSemantic() & g_SoundCVars.g_nDistanceSphereSemantic) != 0) || g_SoundCVars.g_nDistanceSphereSemantic == 0)
									{
										SAuxGeomRenderFlags const nPreviousRenderFlags = pAuxGeom->GetRenderFlags();
										SAuxGeomRenderFlags nNewRenderFlags(e_Def3DPublicRenderflags|e_AlphaBlended);
										nNewRenderFlags.SetCullMode(e_CullModeNone);
										pAuxGeom->SetRenderFlags(nNewRenderFlags);
										pAuxGeom->DrawSphere(vPos, pSound->GetMaxDistance(), ColorB(10, 255, 10, 32));
										pAuxGeom->SetRenderFlags(nPreviousRenderFlags);
									}

									break;
								}
							case 5:
								{
									// Draw the sound's parameter names and their current values.
									if (pSound->IsPlayingOnChannel())
									{
										int nParamIndex         = -1;
										float fValue            = 0.0f;
										float fRangeMin         = 0.0f;
										float fRangeMax         = 0.0f;
										char const* sParamName  = NULL;

										stack_string sText;
										stack_string sTemp;
										sText = sText.Format("%s Vol. on Channel:%1.2f", pSound->GetName(), pSound->GetActiveVolume());

										while (pSound->GetParam(++nParamIndex, &fValue, &fRangeMin, &fRangeMax, &sParamName, false))
										{
											sText += sTemp.Format("\n%s: %.2f Min: %.2f Max: %.2f", sParamName, fValue, fRangeMin, fRangeMax);
										}
										
										bDrawSound = true;
										pRenderer->DrawLabelEx(vPos,1.3f,colorwhite, true, false, sText.c_str());
									}

									break;
								}
							}

							Vec3 vStart, vEnd;
							if (pSound->GetLineSpec(vStart, vEnd))
							{
								ColorB linecol = ColorB( 200, 10, 10, 200 );
								pAuxGeom->DrawLine(vStart, linecol, vEnd, linecol, 2.0f);
							}

							if (bDrawSound)
							{
								float fInner, fOuter;
								pSound->GetConeAngles(fInner, fOuter);
								float innerradius = sinf(fInner*0.5f*3.1415f/180.0f);
								float innerheight = cosf(fInner*0.5f*3.1415f/180.0f);
								//float outerradius = sin(fOuter*0.5f*3.1415/180.0f);
								//float outerheight = cos(fOuter*0.5f*3.1415/180.0f);

								if (innerradius < 0 || innerheight < 0)
									pAuxGeom->DrawSphere( vPos, 0.2f, ColorB( 255, 10, 10, 255 ) );
								else
								{
									//pAuxGeom->DrawCone(vPos, pSound->GetDirection(), outerradius*0.2f, outerheight*0.2f, ColorB( 10, 255, 10, 255 ));
									//pAuxGeom->DrawCone(vPos+pSound->GetDirection()*innerheight*0.2f, -pSound->GetDirection(), innerradius*0.2f, innerheight*0.2f+0.2f, ColorB( 255, 255, 10, 255 ));
									pAuxGeom->DrawCone(vPos+pSound->GetForward()*innerheight*0.2f, -pSound->GetForward(), innerradius*0.2f, innerheight*0.2f, ColorB( 255, 255, 10, 255 ));
								}
							}
						}
					}
				}
			}
		}

		// Drawing a blue radius ball at the sound position and show obstruction test rays
		if (g_nDrawObstruction)
		{
			IRenderAuxGeom* pAuxGeom( pRenderer->GetIRenderAuxGeom() );
			pAuxGeom->SetRenderFlags( e_Def3DPublicRenderflags|e_AlphaBlended );

			float colorwhite[4]={1,1,1,1};
			Vec3	vListener;

			// draw a green ball where a listener is
			tVecListenersIterConst LIterEnd = m_vecListeners.end();
			for (tVecListenersIterConst LIter = m_vecListeners.begin(); LIter != LIterEnd; ++LIter)
			{
				if ((*LIter)->bActive)
				{
					pAuxGeom->DrawSphere( (*LIter)->GetPosition(), 0.2f, ColorB( 10, 255, 10, 255 ) );	
					vListener = (*LIter)->GetPosition();
				}
				else
					pAuxGeom->DrawSphere( (*LIter)->GetPosition(), 0.2f, ColorB( 10, 10, 255, 255 ) );	
				pRenderer->DrawLabelEx((*LIter)->GetPosition(), 1.3f, colorwhite, true, false, "Listener: %d RecordLevel:%1.2f", (*LIter)->nListenerID, (*LIter)->fRecordLevel);
			}

			TSoundIDSet::const_iterator ItEnd = m_currentSoundIDs.end();
			for (TSoundIDSet::const_iterator It=m_currentSoundIDs.begin(); It!=ItEnd; ++It)
			{
				CSound* const pSound = (CSound* const)(GetSound(*It));

				if (!pSound)
					continue;

				if ((pSound->GetFlags() & FLAG_SOUND_OBSTRUCTION) && (pSound->GetFlags() & FLAG_SOUND_3D) && !(pSound->GetFlags() & FLAG_SOUND_2D))
				{
					_smart_ptr<IPlatformSound> pPlatformSound = pSound->GetPlatformSoundPtr();
					SObstruction* pObstruction = pSound->GetObstruction();
					if (pObstruction)
					{
						if (pObstruction->nRaysShot==0)
							continue;

						//if (!pSound->IsPlaying())
						//	continue;

						Vec3 vPos;
						if (pPlatformSound)
						{
							ptParamVEC3F32 TempPos(vPos);
							pPlatformSound->GetParamByType(psp3DPOSITION, &TempPos);
							TempPos.GetValue(vPos);
						}

						vListener.z -= 0.1f;
						float fRadius = sqrtf(pSound->GetMaxDistance());

						pAuxGeom->DrawSphere( pSound->GetPlaybackPosition(), fRadius, ColorF( 0.1f, pObstruction->GetDirect(), pObstruction->GetReverb(), 0.1f+pObstruction->GetDirect()+pObstruction->GetReverb() ) );
						pRenderer->DrawLabelEx(vPos,1.3f,colorwhite, true, false, "DirectOcc :%f \nReverbOcc :%f", pObstruction->GetDirect(), pObstruction->GetReverb());

						for (int i=0; i<pObstruction->nRaysShot; ++i)
						{
							ColorB linecol = ColorB( 200, 10, 10, 200 );

							if (pObstruction->ObstructionTests[i].nHits)
								linecol = ColorB( 200, 10, 10, 200 );
							else
								linecol = ColorB( 200, 200, 10, 200 );

							if (pObstruction->ObstructionTests[i].nTestForTest)
							{
								if (pObstruction->ObstructionTests[i].nHits)
									linecol = ColorB( 100, 100, 100, 200 );
								else
									linecol = ColorB( 200, 200, 200, 200 );
							}

							pAuxGeom->DrawLine(pObstruction->ObstructionTests[i].vOrigin, linecol, pObstruction->ObstructionTests[i].vOrigin+pObstruction->ObstructionTests[i].vDirection, linecol, 10.0f);

						}
					}
				}
			}
		}

		// Draw DynamicReverb debug info
		if (g_SoundCVars.g_fReverbDynamic > 0.0f && g_SoundCVars.g_nReverbDebugDraw > 0 && m_pReverbManager)
		{
			m_pReverbManager->DrawDynamicReverbDebugInfo(pRenderer);
		}

		// Draw VU Meter
		if (_stricmp(g_sVUMeter->GetString(), "") && m_pMoodManager)
			m_pMoodManager->DrawVUMeterMain();
	} // if pRenderer
#endif // INCLUDE_AUDIO_PRODUCTION_CODE
}

// accesses SoundInfos
ISoundProfileInfo* CSoundSystem::GetSoundInfo(int nIndex)
{
	ISoundProfileInfo *pSoundInfo = NULL;

	int nIdx = 0;
	tmapSoundInfo::iterator ItEnd = m_SoundInfos.end();
	for (tmapSoundInfo::const_iterator It = m_SoundInfos.begin(); It!=ItEnd; ++It)
	{
		if (nIndex == nIdx)
			pSoundInfo = (*It).second;
		++nIdx;
	}

	return pSoundInfo;
}

// accesses SoundInfos
ISoundProfileInfo* CSoundSystem::GetSoundInfo(const char* sSoundName)
{
	ISoundProfileInfo *pSoundInfo = NULL;

	tmapSoundInfo::iterator It = m_SoundInfos.find(CONST_TEMP_STRING(sSoundName));
	if (It != m_SoundInfos.end())
	{
		pSoundInfo = (*It).second;
	}

	return pSoundInfo;
}

void CSoundSystem::SetMinMaxRadius(ISound &Sound, float fMin, float fMax)
{
	CSound* const pSound = (CSound* const)(&Sound);
	pSound->SetMinMaxDistance_i(fMin, fMax);
}

//------------------------------------------------------------------------
void CSoundSystem::AddEventListener(ISoundSystemEventListener *pListener, bool bOnlyVoiceSounds)
{
	stl::push_back_unique(m_EventListeners, SEventListener(pListener, bOnlyVoiceSounds));
}

//------------------------------------------------------------------------
void CSoundSystem::RemoveEventListener(ISoundSystemEventListener *pListener)
{
	stl::find_and_erase(m_EventListeners, pListener);
}

//////////////////////////////////////////////////////////////////////////
void CSoundSystem::OnEvent( ESoundSystemCallbackEvent event, ISound *pSound )
{
	if (!pSound) // play safe, avoid compiler warning
		return;

	// send the event to listeners
	if (!m_EventListeners.empty())
	{
		m_EventListenersTemp = m_EventListeners;

		TSoundSystenEventListenerVector::iterator ItEnd = m_EventListenersTemp.end();
		for (TSoundSystenEventListenerVector::iterator It = m_EventListenersTemp.begin(); It != ItEnd; ++It)
		{
			const SEventListener& entry = *It;
			assert (entry.pListener);
			if (!entry.bOnlyVoiceSounds || (pSound && (pSound->GetFlags()&FLAG_SOUND_VOICE)))
				entry.pListener->OnSoundSystemEvent( event, pSound );
		}
	}

//	CSound* pCSound = (CSound*)pSound;
	
	switch(event)
	{
	case SOUNDSYSTEM_EVENT_ON_START:
		break;
	case SOUNDSYSTEM_EVENT_ON_PLAYBACK_STARTED:
		break;
	case SOUNDSYSTEM_EVENT_ON_PLAYBACK_UNPAUSED:
		break;
	case SOUNDSYSTEM_EVENT_ON_STOP:
		break;
	case SOUNDSYSTEM_EVENT_ON_PLAYBACK_PAUSED:
	    break;
	case SOUNDSYSTEM_EVENT_ON_PLAYBACK_STOPPED:
		break;
	default:
	    break;
	}
}

//////////////////////////////////////////////////////////////////////////
void CSoundSystem::OnSystemEvent( ESystemEvent event,UINT_PTR wparam,UINT_PTR lparam )
{
	switch (event)
	{
	case ESYSTEM_EVENT_LEVEL_POST_UNLOAD:
		{
			if (m_pFileCacheManager)
			{
				m_pFileCacheManager->ResetRequestedCounter();
			}

			break;
		}
	case ESYSTEM_EVENT_LEVEL_LOAD_START:
		{
			m_bDelayPrecaching = true;
			Silence(true, true);

			g_nBuffersTrashed = 0;
			g_nOverallStreamsCount = 0;
			g_oSoundMemoryInfo.Reset();

			break;
		}
	case ESYSTEM_EVENT_ACTIVATE: // When Alt+Tabbing out of the application while it's in fullscreen mode ESYSTEM_EVENT_CHANGE_FOCUS does not get called but ESYSTEM_EVENT_ACTIVATE.
		{
			bool bSetVolume = true;

#ifdef INCLUDE_FMOD_EVENTNET_SYSTEM
			bSetVolume = g_nNetworkAudition == 0;
#endif // INCLUDE_FMOD_EVENTNET_SYSTEM

			// Unless we have NetworkAudition enabled turn down the master volume on deactivating+minimizing the window.
			if (bSetVolume)
			{
				// wparam != 0 is active, wparam == 0 is inactive
				// lparam != 0 is minimized, lparam == 0 is not minimized
				m_fNoFocusVolume = (wparam == 0 || lparam != 0) ? g_SoundCVars.g_fNoFocusVolume : 1.0f;
				SetMasterVolume(g_SoundCVars.g_fGameMasterVolume); // Updates the master volume once.
			}

			break;
		}
	case ESYSTEM_EVENT_CHANGE_FOCUS:
		{
			bool bSetVolume = true;

#ifdef INCLUDE_FMOD_EVENTNET_SYSTEM
			bSetVolume = g_nNetworkAudition == 0;
#endif // INCLUDE_FMOD_EVENTNET_SYSTEM

			// Unless we have NetworkAudition enabled turn down the master volume on focus loss.
			if (bSetVolume)
			{
				// wparam != 0 is focused, wparam == 0 is not focused
				m_fNoFocusVolume = (wparam == 0) ? g_SoundCVars.g_fNoFocusVolume : 1.0f;
				SetMasterVolume(g_SoundCVars.g_fGameMasterVolume); // Updates the master volume once.

				// Update the audio device once to ensure the volume changes are properly forwarded to FMOD.
				// This is necessary during level load as the sound system is not updated!
				if (m_pAudioDevice)
				{
					m_pAudioDevice->Update();
				}
			}

			break;
		}
	case ESYSTEM_EVENT_EDITOR_ON_INIT:
	case ESYSTEM_EVENT_LEVEL_GAMEPLAY_START:
		{
			m_bEnteredGame     = true;
			m_bDelayPrecaching = false;

			break;
		}
	case ESYSTEM_EVENT_LEVEL_PRECACHE_END:
		{
			// This event is only of interest when working in the Editor.
			if (gEnv->IsEditor())
			{
				m_bEnteredGame     = true;
				m_bDelayPrecaching = false;
			}

			break;
		}
	case ESYSTEM_EVENT_LEVEL_LOAD_START_LOADINGSCREEN:
	case ESYSTEM_EVENT_LEVEL_UNLOAD:
		{
			m_bEnteredGame = false;

			break;
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CSoundSystem::OnAreaManagerEvent(EEntityEvent event, EntityId TriggerEntityID, IArea* pArea)
{
	switch (event) 
	{
	case ENTITY_EVENT_CROSS_AREA:
		{
			// Here we just either entered or left an area, update the corresponding environment parameter on looped sounds.
			IEntity const* const pTriggerEntity = gEnv->pEntitySystem->GetEntity(TriggerEntityID);

			if (pTriggerEntity && !m_LoopedSounds.empty())
			{
				float const fEnvironment = m_pReverbManager->GetEnvironment(pTriggerEntity->GetWorldPos());
				SSoundContainer::iterator IterEnd(m_LoopedSounds.end());

				for (SSoundContainer::iterator Iter(m_LoopedSounds.begin()); Iter != IterEnd; ++Iter)
				{
					CSound* const pSound = static_cast<CSound* const>((*Iter).get());

					if ((pSound->GetFlagsExtended() & eSFE_ENVIRONMENT_LISTENER) != 0 && pSound->IsActive())
					{
						pSound->SetParam("environment_listener", fEnvironment, false);
					}
				}
			}

			break;
		}
	default:
		{
			break;
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CSoundSystem::Serialize( TSerialize ser )
{
	// cannot change m_lBaseTime, as this is used for async time (which shouldn't be affected by save games)
	if (ser.IsWriting())
	{
		//ser.Value("curTime", m_lCurrentTime);
	}
	else
	{
		//ser.Value("curTime", curTime);
	}

	// Serialize other modules
	
	if (m_pMoodManager)
	{
		m_pMoodManager->SerializeState(ser);
	}

	if (m_pReverbManager)
	{
		m_pReverbManager->SerializeState(ser);
	}

	if (m_pFileCacheManager)
	{
		m_pFileCacheManager->SerializeState(ser);
	}
}

// hack for looping weapon sound MP bug
bool CSoundSystem::IsEventUsedInPlatformSound (void* pHandle)
{
	SSoundContainer::iterator It;
	SSoundContainer::const_iterator itend = m_LoopedSounds.end();

	// check all looping sounds
	for (It=m_LoopedSounds.begin(); It!=itend; ++It)
	{
		CSound* const pSound = (CSound* const)((*It).get());

		if (pSound->GetFlags() & FLAG_SOUND_EVENT)
		{
			IPlatformSound* pPlatformSound = pSound->GetPlatformSoundPtr();

			if (pPlatformSound)
			{
				tSoundHandle pSoundHandle = pPlatformSound->GetSoundHandle();
				if (pSoundHandle)
				{
					if (pHandle == pSoundHandle)
						return true;
				}
			}
		}
	}

	// check also dialog sounds in oneshots as they are based on looping sound definitions
	itend = m_OneShotSounds.end();
	for (It=m_OneShotSounds.begin(); It!=itend; ++It)
	{
		CSound* const pSound = (CSound* const)((*It).get());

		if ((pSound->GetFlags() & FLAG_SOUND_EVENT) && (pSound->GetFlags() & FLAG_SOUND_VOICE))
		{
			IPlatformSound* pPlatformSound = pSound->GetPlatformSoundPtr();

			if (pPlatformSound)
			{
				tSoundHandle pSoundHandle = pPlatformSound->GetSoundHandle();
				if (pSoundHandle)
				{
					if (pHandle == pSoundHandle)
						return true;
				}
			}
		}
	}
	return false;
}

// returns true if called from the main thread for this engine
bool CSoundSystem::IsMainThread() 
{
	return CryGetCurrentThreadId() == m_dwMainThreadId;
}

//////////////////////////////////////////////////////////////////////////
int const CSoundSystem::GetGroupAndEventNameFast(char const* const pcInputPath, char* const pcGroupPathOut, char* const pcEventNameOut) const
{
	if (pcInputPath && pcInputPath[0] && pcGroupPathOut && pcEventNameOut)
	{
		char const* const pcPoint = strstr(pcInputPath, ".");
		char const* const pcColon = strstr(pcInputPath, ":");

		if (!pcPoint && !pcColon)
		{
			// This is a sound key
			return PSNRC_KEY;
		}
		else if (pcPoint && !pcColon)
		{
			// This is a wave file
			return PSNRC_WAV;
		}
		else
		{
			size_t i = 0, j = 0;
			bool bFirstColonFound = false, bSecondColonFound = false;
			size_t const nInputLength = strlen(pcInputPath);

			while (i < nInputLength)
			{
				if (pcInputPath[i] != ':')
				{
					if (!bSecondColonFound)
					{
						if (i < MAX_GROUPPATHLENGTH - 1) // -1 to ensure the last char is always null terminated
							pcGroupPathOut[i] = (char)tolower(pcInputPath[i]);
						else
							return -1;
					}
					else
					{
						if (j < MAX_EVENTNAMELENGTH - 1)
						{
							pcEventNameOut[j] = (char)tolower(pcInputPath[i]);
							++j;
						}
						else
							return -1;
					}

					++i;
				}
				else
				{
					if (bFirstColonFound)
					{
						bSecondColonFound = true;

						if (++i == nInputLength) // Skip the last colon
							return -1; // The string ends with a colon, that's a broken string
					}
					else
					{
						// We hit the first colon and need to copy it into the group path string as well
						if (i < MAX_GROUPPATHLENGTH - 1)
							pcGroupPathOut[i] = (char)tolower(pcInputPath[i]);
						else
							return -1;

						bFirstColonFound = true;
						++i;
					}
				}
			}

			return pcEventNameOut[0] ? PSNRC_EVENT : PSNRC_GROUP;
		}
	}

	return -1;
}

//////////////////////////////////////////////////////////////////////////
void CSoundSystem::GetFullVoicePathFast(char const* const pcVoiceKey, stack_string& rsFullVoicePath) const
{
	// We got a voice key, build the string
	rsFullVoicePath = stack_string(PathUtil::GetLocalizationFolder() + "dialog/");
	rsFullVoicePath	+= pcVoiceKey;
	rsFullVoicePath += GetCurrentCompressionFast();
}

//////////////////////////////////////////////////////////////////////////
bool const CSoundSystem::EntryOrHintHasFlags(char const* const sName, const EAudioFileCacheState eFlags)
{
	return m_pFileCacheManager->EntryHasStateFlags(sName, eFlags);
}

//////////////////////////////////////////////////////////////////////////
void CSoundSystem::CacheAudioFile(char const* const sName, EAudioFileCacheType const eAudioFileCacheType)
{
	m_pFileCacheManager->CacheFile(sName, sName, eAudioFileCacheType);
}

//////////////////////////////////////////////////////////////////////////
void CSoundSystem::RemoveCachedAudioFile(char const* const sName, bool const bNow)
{
#ifdef INCLUDE_AUDIO_PRODUCTION_CODE
	if (g_SoundCVars.g_nFileCacheManagerDebugLog >= 1)
	{
		CryLogAlways("<Sound> AFCM: RemoveEntryOrHint '%s' %d", sName, bNow);
	}
#endif // INCLUDE_AUDIO_PRODUCTION_CODE

	m_pFileCacheManager->RemoveEntryOrHint(sName, bNow);
}

//////////////////////////////////////////////////////////////////////////
void CSoundSystem::CacheAudioData(char const* const sLevelName)
{
	m_pFileCacheManager->ParsePreloadsXML(sLevelName);
}

//////////////////////////////////////////////////////////////////////////
void CSoundSystem::ClearAudioDataCache(EAudioFileCacheType const eAudioFileCacheType /* = eAFCT_ALL */, bool bDefrag)
{
	m_pFileCacheManager->Clear(eAudioFileCacheType);







}

#ifdef INCLUDE_AUDIO_PRODUCTION_CODE
//////////////////////////////////////////////////////////////////////////
void CSoundSystem::AddLoadRequestInfo(size_t const nThreadID, char const* const sSoundBankName, char const* const sEventName)
{
	if (g_nSoundInfo && nThreadID < (size_t)g_SoundCVars.g_nNumLoadingThreadsToUse)
	{
		ScopedSwitchToGlobalHeap useGlobalHeap;
		m_aLoadRequestInfos.push_back(SLoadRequestInfo(sSoundBankName, sEventName, gEnv->pTimer->GetFrameStartTime(), nThreadID));
	}
}
#endif // INCLUDE_AUDIO_PRODUCTION_CODE

//////////////////////////////////////////////////////////////////////////
void CSoundSystem::OnCVarChanged(ICVar* const pCVar)
{
	if (pCVar)
	{
		if (m_pAudioDevice)
		{
			m_pAudioDevice->OnCVarChanged(pCVar);
		}

		if (m_pFileCacheManager)
		{
			m_pFileCacheManager->OnCVarChanged(pCVar);
		}

		stack_string const sTemp(pCVar->GetName());

		if (sTemp.compareNoCase("s_GameMasterVolume") == 0)
		{
			g_SoundCVars.g_fGameMasterVolume = clamp_tpl(g_SoundCVars.g_fGameMasterVolume, 0.0f, 1.0f);
			SetMasterVolume(g_SoundCVars.g_fGameMasterVolume);
		}
		else if (sTemp.compareNoCase("s_GameSFXVolume") == 0)
		{
			g_SoundCVars.g_fGameSFXVolume = clamp_tpl(g_SoundCVars.g_fGameSFXVolume, 0.0f, 1.0f);
			m_fGameSFXRatio = g_SoundCVars.g_fGameSFXVolume;
			g_SoundCVars.g_fGameSFXVolume *= g_SoundCVars.g_fGameMasterVolume;
		}
		else if (sTemp.compareNoCase("s_GameMusicVolume") == 0)
		{
			g_SoundCVars.g_fGameMusicVolume = clamp_tpl(g_SoundCVars.g_fGameMusicVolume, 0.0f, 1.0f);
			m_fGameMusicRatio = g_SoundCVars.g_fGameMusicVolume;
			g_SoundCVars.g_fGameMusicVolume *= g_SoundCVars.g_fGameMasterVolume;
		}
		else if (sTemp.compareNoCase("s_GameDialogVolume") == 0)
		{
			g_SoundCVars.g_fGameDialogVolume = clamp_tpl(g_SoundCVars.g_fGameDialogVolume, 0.0f, 1.0f);
			m_fGameDialogRatio = g_SoundCVars.g_fGameDialogVolume;
			g_SoundCVars.g_fGameDialogVolume *= g_SoundCVars.g_fGameMasterVolume;
		}
		else if (sTemp.compareNoCase("s_ReverbType") == 0)
		{
			if (g_SoundCVars.g_nReverbType < 0 || g_SoundCVars.g_nReverbType > 2)
			{
				// Keep current value in case we're trying to set outside allowed values.
				g_SoundCVars.g_nReverbType = m_nReverbType;
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
#ifndef EXCLUDE_SCALEFORM_SDK

namespace CryVideoSoundSystem
{
	template<typename Base, typename Instance>
	struct DelegateFactory : public Base
	{
		static Instance* Create(IAllocatorDelegate* pAllocator, const Instance& src)
		{
			if (!pAllocator)
				return 0;

			Instance* p = (Instance*) pAllocator->Allocate(sizeof(Instance));
			new(p) Instance(src);
			return p;
		}

		static void Destroy(Instance* p)
		{
			if (p)
			{
				IAllocatorDelegate* pAllocator = p->m_pAllocator;
				p->~Instance();
				pAllocator->Free(p);
			}
		}
	};

	//////////////////////////////////////////////////////////////////////////
	struct ChannelDelegate : DelegateFactory<IChannelDelegate, ChannelDelegate>
	{
		template<typename Base, typename Instance> friend struct DelegateFactory;

	public:
		virtual void Release()
		{
			Destroy(this);
		}

		virtual bool Stop()
		{
			return m_pChannel && FMOD_OK == m_pChannel->stop();
		}

		virtual bool SetPaused(bool pause)
		{
			return m_pChannel && FMOD_OK == m_pChannel->setPaused(pause);
		}

		virtual bool SetVolume(float volume)
		{
			return m_pChannel && FMOD_OK == m_pChannel->setVolume(volume);
		}

		virtual bool Mute(bool mute)
		{
			return m_pChannel && FMOD_OK == m_pChannel->setMute(mute);
		}

		virtual bool SetBytePosition(unsigned int bytePos)
		{
			return m_pChannel && FMOD_OK == m_pChannel->setPosition(bytePos, FMOD_TIMEUNIT_PCMBYTES);
		}

		virtual bool GetBytePosition(unsigned int& bytePos)
		{
			return m_pChannel && FMOD_OK == m_pChannel->getPosition(&bytePos, FMOD_TIMEUNIT_PCMBYTES);
		}

	public:
		static ChannelDelegate* CreateInstance(IAllocatorDelegate* pAllocator, FMOD::Channel* pChannel)
		{
			ChannelDelegate* const pChannelDelegate = Create(pAllocator, ChannelDelegate(pAllocator, pChannel));

			if (pChannelDelegate && pChannel)
			{
				// Boost priority.
				pChannel->setPriority(0);

				// Disable reverb.
				FMOD_REVERB_CHANNELPROPERTIES oReverbProperties = {0};
				FMOD_RESULT eResult = pChannel->getReverbProperties(&oReverbProperties);
				assert(eResult == FMOD_OK);
				oReverbProperties.Room = -10000;
				eResult = pChannel->setReverbProperties(&oReverbProperties);
				assert(eResult == FMOD_OK);

				// If desired we can make this channel part of a category to play through.
				/*FMOD::EventSystem* const pEventSystem = static_cast<FMOD::EventSystem* const>(gEnv->pSoundSystem->GetIAudioDevice()->GetEventSystem());

				if (pEventSystem)
				{
					FMOD::EventCategory* pMasterCategory = NULL;
					FMOD::EventCategory* pMusicCategory  = NULL;
					eResult = pEventSystem->getCategory("master", &pMasterCategory);
					assert(eResult == FMOD_OK);

					if (eResult == FMOD_OK)
					{
						eResult = pMasterCategory->getCategory("music", &pMusicCategory);
						assert(eResult == FMOD_OK);

						if (eResult == FMOD_OK)
						{
							FMOD::ChannelGroup* pChannelGroup = NULL;
							eResult = pMusicCategory->getChannelGroup(&pChannelGroup);
							assert(eResult == FMOD_OK);
							eResult = pChannel->setChannelGroup(pChannelGroup);
							assert(eResult == FMOD_OK);
						}
					}
				}*/
			}

			return pChannelDelegate;
		}

	protected:
		ChannelDelegate(IAllocatorDelegate* pAllocator, FMOD::Channel* pChannel)
		: m_pAllocator(pAllocator)
		, m_pChannel(pChannel)
		{
			assert(m_pAllocator);
			assert(m_pChannel);
		}
		virtual ~ChannelDelegate() {}

	protected:
		IAllocatorDelegate* m_pAllocator;
		FMOD::Channel* m_pChannel;
	};

	//////////////////////////////////////////////////////////////////////////
	struct SoundDelegate : public DelegateFactory<ISoundDelegate, SoundDelegate>
	{
		template<typename Base, typename Instance> friend struct DelegateFactory;

	public:
		virtual void Release()
		{
			Destroy(this);
		}

		virtual IChannelDelegate* Play()
		{
			FMOD::Channel* pChannel = 0;
			if (!m_pFMOD || m_pFMOD->playSound(FMOD_CHANNEL_FREE, m_pSound, false, &pChannel) != FMOD_OK)
				return 0;

			return ChannelDelegate::CreateInstance(m_pAllocator, pChannel);
		}

		virtual bool Lock(unsigned int offset, unsigned int length, LockRange& lr)
		{
			return m_pSound ? m_pSound->lock(offset, length, &lr.p0, &lr.p1, &lr.length0, &lr.length1) == FMOD_OK : false;
		}

		virtual bool Unlock(const LockRange& lr)
		{
			return m_pSound ? m_pSound->unlock(lr.p0, lr.p1, lr.length0, lr.length1) == FMOD_OK : false;
		}

	public:
		static SoundDelegate* CreateInstance(IAllocatorDelegate* pAllocator, FMOD::System* pFMOD, FMOD::Sound* pSound)
		{
			return Create(pAllocator, SoundDelegate(pAllocator, pFMOD, pSound));
		}

	protected:
		SoundDelegate(IAllocatorDelegate* pAllocator, FMOD::System* pFMOD, FMOD::Sound* pSound)
		: m_pAllocator(pAllocator)
		, m_pFMOD(pFMOD)
		, m_pSound(pSound)
		{
			assert(m_pAllocator);
			assert(m_pFMOD);
			assert(m_pSound);
		}

		SoundDelegate(const SoundDelegate& rhs)
		: m_pAllocator(rhs.m_pAllocator)
		, m_pFMOD(rhs.m_pFMOD)
		, m_pSound(rhs.m_pSound)
		{
			// transfer sound ownership
			SoundDelegate& _rhs = const_cast<SoundDelegate&>(rhs);
			_rhs.m_pSound = 0;
		}

		virtual ~SoundDelegate()
		{
			if (m_pSound)
			{
				m_pSound->release();
				m_pSound = 0;
			}
		}

	protected:
		IAllocatorDelegate* m_pAllocator;
		FMOD::System* m_pFMOD;
		FMOD::Sound* m_pSound;
	};

	//////////////////////////////////////////////////////////////////////////
	struct PlayerDelegate : public DelegateFactory<IPlayerDelegate, PlayerDelegate>
	{
		template<typename Base, typename Instance> friend struct DelegateFactory;

	public:
		virtual void Release()
		{
			Destroy(this);
		}

		virtual ISoundDelegate* CreateSound(unsigned int numChannels, unsigned int sampleRate, unsigned int lengthInBytes)
		{
			if (!m_pFMOD)
				return 0;

			FMOD_CREATESOUNDEXINFO exinfo;
			memset(&exinfo, 0, sizeof(FMOD_CREATESOUNDEXINFO));
			exinfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO);
			exinfo.length = lengthInBytes;
			exinfo.numchannels = numChannels;
			exinfo.defaultfrequency = sampleRate;
			exinfo.format = FMOD_SOUND_FORMAT_PCM16;

			FMOD_MODE const flags = FMOD_2D | FMOD_SOFTWARE | FMOD_OPENUSER | FMOD_LOOP_NORMAL | FMOD_LOWMEM;

			FMOD::Sound* pSound = 0;
			if (m_pFMOD->createSound(0, flags, &exinfo, &pSound) != FMOD_OK)
			{
				if (pSound)
					pSound->release();
				return 0;
			}

			unsigned int len = 0;
			if (pSound->getLength(&len, FMOD_TIMEUNIT_PCMBYTES) != FMOD_OK || len != lengthInBytes)
			{
				pSound->release();
				return 0;
			}

			return SoundDelegate::CreateInstance(m_pAllocator, m_pFMOD, pSound);
		}

		virtual bool MuteMainTrack() const
		{









			return false;

		}

	public:
		static PlayerDelegate* CreateInstance(IAllocatorDelegate* pAllocator, FMOD::System* pFMOD)
		{
			return Create(pAllocator, PlayerDelegate(pAllocator, pFMOD));
		}

	protected:
		PlayerDelegate(IAllocatorDelegate* pAllocator, FMOD::System* pFMOD)
		: m_pAllocator(pAllocator)
		, m_pFMOD(pFMOD)
		{
			assert(m_pAllocator);
			assert(m_pFMOD);
		}
		virtual ~PlayerDelegate() {}

	protected:
		IAllocatorDelegate* m_pAllocator;
		FMOD::System* m_pFMOD;
	};

} // namespace CryVideoSoundSystem

CryVideoSoundSystem::IPlayerDelegate* CSoundSystem::CreateVideoSoundSystem(CryVideoSoundSystem::IAllocatorDelegate* pAllocator) const
{
	FMOD::System* pFMOD = (FMOD::System*) (m_pAudioDevice ? m_pAudioDevice->GetSoundLibrary() : 0);
	return pFMOD && pAllocator ? CryVideoSoundSystem::PlayerDelegate::CreateInstance(pAllocator, pFMOD) : 0;
}

#endif // #ifndef EXCLUDE_SCALEFORM_SDK

#include UNIQUE_VIRTUAL_WRAPPER(ISoundProfileInfo)
